Сбой IF в пакетном файле Windows - Как 30000000000000 может равняться 40000000000?
ЕСЛИ дает неправильный ответ, когда я пытаюсь сравнить 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 ответ
Если обе стороны сравнения 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"
Обычно мы не имеем дело с большими числами в пакетных файлах. Но они могут легко появиться, если мы посмотрим на свободное место на жестком диске. Терабайтные диски сейчас относительно недороги. Вот как я впервые столкнулся с сравнением больших чисел на /questions/14720447/sohranit-znacheniya-komandnogo-fajla/14720453#14720453
Я решил поддерживать 15 цифр в моем примере, потому что это соответствует почти 999 терабайтам. Я предполагаю, что пройдет некоторое время, прежде чем мы будем иметь дело с дисками большего размера. (Но кто знает!)
РЕДАКТИРОВАТЬ - Мое описание того, как IF анализирует числа преднамеренно упрощенно. IF фактически поддерживает отрицательные числа, а также шестнадцатеричные и восьмеричные обозначения. См. Правила того, как CMD.EXE анализирует числа для более подробного объяснения.