IF的结果很奇怪

3
这个问题和它的答案的启发,我做了一些测试。 一个答案表明,数字太大(超出32位整数)并被截断,但这并不能解释结果。 显然也不能将两侧均视为字符串(我本来以为会这样)。 看起来if混乱了,并认为“好吧,我不知道-给它一个TRUE”。 (使用neqgtrlss而不是equgeqleq总是返回FALSE)。

如果任何一个a和/或b在32位整数的边界内或包含[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 -)

这件事情有没有逻辑上的解释,还是我们必须在不思考的情况下接受它?


你希望发生什么? 链接回“此问题”无法提供上下文,以使其成为独立的问题。 - Teepeemm
我期望的是字符串比较或错误消息,但不是同时出现“string1不等于且不小于且不大于string2”的情况(显然它不是数字(在cmd工作方式的意义上 - 这意味着不是32位INT),我希望它被视为字符串处理)。 echo on 明确显示没有截断或转换发生,因此似乎不是 cmd 解析问题,而是 if 本身的问题。 我想了解发生了什么以及为什么。 - Stephan
1
相关问题 - Stephan
2个回答

4
这个结果的原因可以在strtol函数的文档中找到,该函数首先使用比较运算符EQUNEQLSSLEQGTRGEQ,如我在Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files上的回答中所解释的那样。

返回值
成功时,该函数将转换后的整数作为long int值返回。
如果无法执行有效的转换,则返回值为零(0L)。
如果读取的值超出了long int可表示的范围,则函数返回LONG_MAX或LONG_MIN(定义在<climits>中),并且errno设置为ERANGE。

最后一句话是最重要的。

看起来cmd.exe中的IF与此C代码类似:

#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 != '\0')) ||
           ((lIntegerValue2 == 0) && (*pcEndValue2 != '\0')))
        {
            /* 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):\n\n",argv[1],argv[2]);
            printf("a GEQ b: %s\n",(iStringResult1 >= 0) ? csYes : csNo);
            printf("b GEQ a: %s\n",(iStringResult2 >= 0) ? csYes : csNo);
            printf("a LEQ b: %s\n",(iStringResult1 <= 0) ? csYes : csNo);
            printf("b LEQ a: %s\n",(iStringResult2 <= 0) ? csYes : csNo);
            printf("a EQU b: %s\n",(iStringResult2 == 0) ? csYes : csNo);
            iExitCode = 1;
        }
        else
        {
            /* Compare the values. */
            printf("Value comparing %s/%ld (a) with %s/%ld (b):\n\n",argv[1],lIntegerValue1,argv[2],lIntegerValue2);
            printf("a GEQ b: %s\n",(lIntegerValue1 >= lIntegerValue2) ? csYes : csNo);
            printf("b GEQ a: %s\n",(lIntegerValue2 >= lIntegerValue1) ? csYes : csNo);
            printf("a LEQ b: %s\n",(lIntegerValue1 <= lIntegerValue2) ? csYes : csNo);
            printf("b LEQ a: %s\n",(lIntegerValue2 <= lIntegerValue1) ? csYes : csNo);
            printf("a EQU b: %s\n",(lIntegerValue2 == lIntegerValue1) ? csYes : csNo);
            iExitCode = 0;
        }
        printf("\nError number a: %d ... %s\n",iErrorNumber1,strerror(iErrorNumber1));
        printf("Error number b: %d ... %s\n",iErrorNumber2,strerror(iErrorNumber2));
    }
    return iExitCode;
}

将此C代码编译为控制台应用程序,并使用参数333333333333 444444444444运行可执行文件的结果,例如输出:

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)

运行带有参数333333333333 222222222222的可执行文件,例如会产生如下输出:

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)

注意:错误编号和相应的错误字符串可能因所使用的C编译器或标准库而异。

在两个测试案例中,两个参数都导致从字符串转换为长整型时发生32位有符号整数溢出。因此,strtol对所有四个值返回LONG_MAX并将errno设置为ERANGE。但是,在cmd.exeIF代码中没有评估溢出条件。它只检查转换结果以及结束指针对于两个参数指向的字符,就像上面的C代码一样。

换句话说,只要由于参数字符串中存在无效字符而导致从字符串到整数的转换不失败,IF在使用比较运算符EQUNEQLSSLEQGTRGEQ时总是进行整数比较。超出范围的情况不是IF不执行整数比较的理由。

仅当两个参数字符串中有一个包含整数无效字符时才进行字符串比较。


2
是的 - 这就是我在寻找的东西!正如我所怀疑的那样,if语句的程序员有点懒... - Stephan

1
这是一个限制。
C:>set /A a=333333333333
Invalid number.  Numbers are limited to 32-bits of precision.

是的,我完全了解32位整数及其限制。但我的问题不是关于set /a,而是关于if。当if“检测到”非(INT32)数字时,它不应该使用字符串比较吗?(是的,我知道,将两侧放在引号中会_强制_进行字符串比较,但这不是我的问题) - Stephan
1
将大数字转换为字符串比较可能是一件理想的事情。但这似乎不是 cmd 所做的。如果代码有可能出现如此大的数字,为什么不引用它们并继续进行呢?你是在考虑从时钟上节省几微秒吗?如果是这样,应该选择另一种语言。 - lit
cmd 将其视为字符串处理 - 如果不是,则会出现语法或溢出错误。此外,echo on 显示它被解析为原样。是 if 本身无法处理它,并且始终使用 equgeqleq 给出 TRUE,始终使用 equgeqleq 给出 FALSE。我想了解其中的原因。 - Stephan
1
我认为有可能 cmd 没有将其作为字符串处理。如果将其作为数字处理,则超出范围。对两个超出范围的数字进行比较运算可能是没有意义的。我本来期望会有一个错误消息,但那样反而会更有帮助。 - lit
一个超出范围的数字应该在解析阶段就报错(甚至在传递给 if 之前),所以我认为它肯定是一个字符串。 - Stephan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接