Странные результаты с IF
Вдохновленный этим вопросом и его ответами, я провел некоторое тестирование. Один ответ предполагает, что числа слишком велики (из 32-битного целого), и они усекаются, но это не объясняет результаты. Также очевидно, что он не сравнивает обе стороны как строки (как я и ожидал). Кажется, что if
запутывается и думает: "Ну, я не знаю - дай ИСТИНУ". (С помощью neq
, gtr
, lss
вместо equ
, geq
, leq
всегда дают ЛОЖЬ).
Код работает как положено, если любой из 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 -)
Есть ли какое-то логическое объяснение этому, или это просто то, с чем мы должны жить, не задумываясь?
2 ответа
Причину этого результата можно найти в документации по функции strtol, которая используется в первую очередь при использовании операторов сравнения. EQU
, NEQ
, LSS
, LEQ
, GTR
, GEQ
как объяснено в моем ответе на Symbol, эквивалентном NEQ, LSS, GTR и т. д. в пакетных файлах Windows.
Возвращаемое значение
В случае успеха функция возвращает преобразованное целое число как длинное значение типа int.
Если действительное преобразование не может быть выполнено, возвращается нулевое значение (0L).
Если считываемое значение выходит за пределы диапазона представимых значений с помощью длинного целого, функция возвращает LONG_MAX или LONG_MIN (определенный в;), а для errno задано значение ERANGE.
Последнее предложение является наиболее важным здесь.
Похоже, если в cmd.exe
закодирован как этот код 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-разрядному целочисленному переполнению со знаком при преобразовании из строки в long int. Следовательно strtol
возвращается для всех четырех значений LONG_MAX
и установить errno
в ERANGE
, Но условие переполнения не оценивается кодом IF в cmd.exe
, Просто проверяется результат преобразования и на какой символ указывает указатель конца для обоих аргументов, как в коде C выше.
Другими словами, IF обрабатывает использование операторов сравнения EQU
, NEQ
, LSS
, LEQ
, GTR
, GEQ
всегда целочисленное сравнение, пока преобразование из строки в целое число не завершится неудачно ни для одного из двух аргументов из-за недопустимого символа в строках аргумента. Условие вне диапазона не является причиной, если IF не выполняет целочисленное сравнение.
Сравнение строк выполняется только в том случае, если одна из двух строк аргумента содержит недопустимый символ для целого числа.
Это предел.
C:>set /A a=333333333333
Invalid number. Numbers are limited to 32-bits of precision.