Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
631 views
in Technique[技术] by (71.8m points)

if statement - weird results with IF

Inspired by this question and it's answers, I did some testing. One answer suggests, the numbers are too big (out of 32bit integer) and they get truncated, but this doesn't explain the results. Also obviously it doesn't compare both sides as strings (as I would have expected). It seems that if gets confused and thinks "well, I don't know - give it a TRUE". (Using neq, gtr, lss instead of equ, geq, leq always give FALSE).

The code works as expected, if any of a and/or b are within the borders of 32bit integer or contain any char out of [0-9].

@echo off
set a=333333333333
set b=444444444444
call :compare
set b=222222222222
call :compare
goto :eof

:compare
echo comparing %a% with %b%
if %a% geq %b% (echo a ^>= b) else (echo -)
if %b% geq %a% (echo b ^>= a) else (echo -)
if %a% leq %b% (echo a ^<= b) else (echo -)
if %b% leq %a% (echo b ^<= a) else (echo -)
if %a% equ %b% (echo a  = b) else (echo -)
if %a% == %b% (echo a == b) else (echo -)

Is there any logical explanation for this, or is it just something we have to live with without thinking?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The reason for this result can be found in documentation of function strtol which is used first on using the comparison operators EQU, NEQ, LSS, LEQ, GTR, GEQ as explained in my answer on Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files.

Return Value
On success, the function returns the converted integral number as a long int value.
If no valid conversion could be performed, a zero value is returned (0L).
If the value read is out of the range of representable values by a long int, the function returns LONG_MAX or LONG_MIN (defined in <climits>), and errno is set to ERANGE.

The last sentence is most important here.

It looks like IF in cmd.exe is coded similar to this C code:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (int argc, char* argv[])
{
    const char csNo[] =  "no";
    const char csYes[] = "yes";

    char* pcEndValue1;
    char* pcEndValue2;
    int   iExitCode = 2;
    int   iErrorNumber1;
    int   iErrorNumber2;
    int   iStringResult1;
    int   iStringResult2;
    long  lIntegerValue1;
    long  lIntegerValue2;

    if(argc > 2)
    {
        /* Convert the two arguments to 32-bit signed integers. */
        lIntegerValue1 = strtol(argv[1],&pcEndValue1,0);
        iErrorNumber1 = errno;
        lIntegerValue2 = strtol(argv[2],&pcEndValue2,0);
        iErrorNumber2 = errno;

        /* Failed the conversion for any of the two arguments? */
        if(((lIntegerValue1 == 0) && (*pcEndValue1 != '')) ||
           ((lIntegerValue2 == 0) && (*pcEndValue2 != '')))
        {
            /* Compare case-sensitive the two arguments as strings. */
            iStringResult1 = strcmp(argv[1],argv[2]);
            iStringResult2 = strcmp(argv[2],argv[1]);

            printf("String comparing %s (a) with %s (b):

",argv[1],argv[2]);
            printf("a GEQ b: %s
",(iStringResult1 >= 0) ? csYes : csNo);
            printf("b GEQ a: %s
",(iStringResult2 >= 0) ? csYes : csNo);
            printf("a LEQ b: %s
",(iStringResult1 <= 0) ? csYes : csNo);
            printf("b LEQ a: %s
",(iStringResult2 <= 0) ? csYes : csNo);
            printf("a EQU b: %s
",(iStringResult2 == 0) ? csYes : csNo);
            iExitCode = 1;
        }
        else
        {
            /* Compare the values. */
            printf("Value comparing %s/%ld (a) with %s/%ld (b):

",argv[1],lIntegerValue1,argv[2],lIntegerValue2);
            printf("a GEQ b: %s
",(lIntegerValue1 >= lIntegerValue2) ? csYes : csNo);
            printf("b GEQ a: %s
",(lIntegerValue2 >= lIntegerValue1) ? csYes : csNo);
            printf("a LEQ b: %s
",(lIntegerValue1 <= lIntegerValue2) ? csYes : csNo);
            printf("b LEQ a: %s
",(lIntegerValue2 <= lIntegerValue1) ? csYes : csNo);
            printf("a EQU b: %s
",(lIntegerValue2 == lIntegerValue1) ? csYes : csNo);
            iExitCode = 0;
        }
        printf("
Error number a: %d ... %s
",iErrorNumber1,strerror(iErrorNumber1));
        printf("Error number b: %d ... %s
",iErrorNumber2,strerror(iErrorNumber2));
    }
    return iExitCode;
}

Compiling this C code as console application and running the executable with the parameters 333333333333 444444444444 results for example in output:

Value comparing 333333333333/2147483647 (a) with 444444444444/2147483647 (b):

a GEQ b: yes
b GEQ a: yes
a LEQ b: yes
b LEQ a: yes
a EQU b: yes

Error number a: 2 ... Output of function out of range (ERANGE)
Error number b: 2 ... Output of function out of range (ERANGE)

And running the executable with the parameters 333333333333 222222222222 results for example in output:

Value comparing 333333333333/2147483647 (a) with 222222222222/2147483647 (b):

a GEQ b: yes
b GEQ a: yes
a LEQ b: yes
b LEQ a: yes
a EQU b: yes

Error number a: 2 ... Output of function out of range (ERANGE)
Error number b: 2 ... Output of function out of range (ERANGE)

Note: The error number and the corresponding error string can differ depending on used C compiler respectively standard library.

In both test cases both arguments resulted in a 32-bit signed integer overflow on conversion from string to long int. Therefore strtol returned for all four values LONG_MAX and set errno to ERANGE. But the overflow condition is not evaluated by code of IF in cmd.exe. It is just checked the conversion result and on which character the end pointer points to for both arguments like by the C code above.

In other words IF processes on usage of comparison operators EQU, NEQ, LSS, LEQ, GTR, GEQ always an integer comparison as long as conversion from string to integer does not fail for any of the two arguments because of an invalid character in argument strings. An out of range condition is no reason for IF not doing an integer comparison.

A string comparison is done only if one of the two argument strings contains an invalid character for an integer.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...