Сбой IF в пакетном файле Windows - Как 30000000000000 может равняться 40000000000? - PullRequest
12 голосов
/ 02 февраля 2012

ЕСЛИ дает неправильный ответ, когда я пытаюсь сравнить 2 больших числа.

Например, этот простой пакетный файл

@echo off
setlocal
set n1=30000000000000
set n2=40000000000
if %n1% gtr %n2% echo %n1% is greater than %n2%
if %n1% lss %n2% echo %n1% is less than %n2%
if %n1% equ %n2% echo %n1% is equal to %n2%

производит

30000000000000 is equal to 40000000000

Чтопроисходит, и как мне это исправить?

1 Ответ

32 голосов
/ 02 февраля 2012

Если обе стороны сравнения IF состоят строго из десятичных цифр, тогда IF будет интерпретировать обе стороны как числа.Это то, что позволяет IF правильно определить, что 10 больше 9. Если у вас есть любые нецифровые символы, тогда IF выполняет сравнение строк.Например, «10» меньше, чем «9», потому что кавычки не являются цифрами, а 1 сортирует ниже, чем 9.

Причина, по которой сравнение в вопросе не удается, состоит в том, что CMD.EXE не может обрабатывать числа больше2147483647. Причудливый странный дизайн в IF обрабатывает любое число больше 2147483647 как равное 2147483647.

Если вы хотите провести сравнение строк больших чисел, решение будет простым.Вам просто нужно добавить 1 или более нецифровых символов в обе стороны условия.Следующий скрипт -

@echo off
setlocal
set n1=30000000000000
set n2=40000000000
if "%n1%" gtr "%n2%" echo "%n1%" is greater than "%n2%"
if "%n1%" lss "%n2%" echo "%n1%" is less than "%n2%"
if "%n1%" equ "%n2%" echo "%n1%" is equal to "%n2%"

дает правильный результат сравнения строк

"30000000000000" is less than "40000000000"

Но в большинстве случаев это не то, что нужно.

Если вы хотитесделайте числовое сравнение, тогда процесс будет немного более сложным.Вам необходимо преобразовать число в строку, которая будет правильно сортироваться как число.Это достигается путем добавления префикса к числовой строке с нулями таким образом, чтобы обе числовые строки имели одинаковую ширину.Самое простое решение - определить максимальное количество цифр, которое вам нужно поддерживать - скажем, 15 для этого примера.Таким образом, вы добавляете к каждому значению 15 нулей, а затем сохраняете только самые правые 15 символов, используя операцию подстроки.Вы также должны добавить нецифровое число к обеим сторонам, как и раньше - опять-таки кавычки работают хорошо.

Этот скрипт -

@echo off
setlocal
set n1=30000000000000
set n2=40000000000
call :padNum n1
call :padNum n2
if "%n1%" gtr "%n2%" echo %n1% is greater than %n2%
if "%n1%" lss "%n2%" echo %n1% is less than %n2%
if "%n1%" equ "%n2%" echo %n1% is equal to %n2%
exit /b

:padNum
setlocal enableDelayedExpansion
set "n=000000000000000!%~1!"
set "n=!n:~-15!"
endlocal & set "%~1=%n%"
exit /b

производит -

030000000000000 is greater than 000040000000000

Примечаниеоставленный префикс с пробелами работает так же хорошо, как и с нулями.

Позже вы можете удалить начальные нули, когда захотите, используя следующее (или адаптироваться для удаления начальных пробелов)

for /f "tokens=* delims=0" %%A in ("%n1%") do set "n1=%%A"
if not defined n1 set "n1=0"

Обычно мыне работайте с большими числами в пакетных файлах.Но они могут легко появиться, если мы посмотрим на свободное место на жестком диске.Терабайтные диски сейчас относительно недороги.Именно так я впервые столкнулся с сравнением больших чисел на https://stackoverflow.com/a/9099542/1012053

. Я выбрал поддержку 15 цифр в моем примере, потому что это эквивалентно почти 999 терабайтам.Я предполагаю, что пройдет некоторое время, прежде чем мы будем иметь дело с дисками большего размера.(Но кто знает!)

РЕДАКТИРОВАТЬ - Мое описание того, как IF анализирует числа, преднамеренно упрощено.IF фактически поддерживает отрицательные числа, а также шестнадцатеричные и восьмеричные обозначения.См. Правила того, как CMD.EXE анализирует числа для более подробного объяснения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...