2012-02-02 6 views
10

IF podaje niewłaściwą odpowiedź, gdy próbuję porównać 2 duże liczby.Windows plik wsadowy IF błąd - w jaki sposób 30000000000000 równy 40000000000?

Na przykład ten prosty plik wsadowy

@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% 

produkuje

30000000000000 is equal to 40000000000 

Co się dzieje i jak to naprawić?

Odpowiedz

30

Jeśli obie strony porównania IF składają się wyłącznie z cyfr dziesiętnych, to IF interpretuje obie strony jako liczby. To właśnie pozwala IF poprawnie ustalić, że 10 jest większe niż 9. Jeśli masz jakieś znaki nie będące cyframi, to IF wykonuje porównanie ciągów. Na przykład "10" jest mniejsze niż "9", ponieważ cudzysłowy nie są cyframi, a 1 rodzaje są niższe niż 9.

Przyczyną niepowodzenia porównania w pytaniu jest to, że CMD.EXE nie może przetwarzać liczb większych niż 2147483647. Dziwne dziwactwo projektu w IF traktuje dowolną liczbę większą niż 2147483647 jako równą 2147483647.

Jeśli chcesz dokonać porównania łańcuchów liczb dużych, to rozwiązanie jest proste. Musisz tylko dodać 1 lub więcej znaków bez cyfr po obu stronach warunku. Poniższy skrypt -

@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%" 

wytwarza prawidłowe porównanie ciąg spowodować

"30000000000000" is less than "40000000000" 

ale w większości przypadków nie jest to, co się chce.

Jeśli chcesz wykonać porównanie numeryczne, proces ten jest nieco bardziej zaangażowany. Musisz przekonwertować liczbę na ciąg, który będzie odpowiednio sortować jako liczbę. Osiąga się to przez prefiksowanie ciągu liczbowego zerami w taki sposób, aby oba łańcuchy numeryczne miały taką samą szerokość. Najprostszym rozwiązaniem jest określenie maksymalnej liczby cyfr, które należy obsłużyć - powiedzmy 15 dla tego przykładu. Dlatego przedrostkiem każdej wartości jest 15 zer, a następnie zachowaj tylko 15 najbardziej odpowiednich z 15 znaków za pomocą operacji podciągu. Musisz również dodać cyfry bez znaku do obu stron, tak jak poprzednio - znowu cytaty działają dobrze.

Ten skrypt -

@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 

produkuje -

030000000000000 is greater than 000040000000000 

Zauważ, że lewy poprzedzając spacjami działa tak samo dobrze jak zerami.

można później usunąć zera, kiedy tylko chcesz przy użyciu następujących (lub dostosować usunąć spacje)

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

Normalnie nie mamy do czynienia z dużymi numerami w plikach wsadowych. Ale mogą łatwo wyskoczyć, jeśli spojrzymy na wolne miejsce na dysku twardym. Terabajtowe dyski są teraz stosunkowo niedrogie. W ten sposób po raz pierwszy spotkałem się z porównywaniem dużych liczb pod numerem https://stackoverflow.com/a/9099542/1012053

Wybrałem obsługę 15 cyfr w moim przykładzie, ponieważ to równa się prawie 999 terabajtów. Wyobrażam sobie, że minie trochę czasu, zanim będziemy mieli do czynienia z większymi dyskami. (Ale kto wie!)

EDIT - Mój opis sposobu Jeśli numery analizuje celowo nadmiernie uproszczone. IF faktycznie obsługuje liczby ujemne, a także notację szesnastkową i ósemkową. Zobacz Rules for how CMD.EXE parses numbers, aby uzyskać bardziej szczegółowe wyjaśnienie.

+0

to było naprawdę świetne. dzięki – Lupocci

+0

Porównania numerycznego dokonuje się, jeśli obie strony są liczbami dobrze uformowanymi - nie tylko ściśle dziesiętnymi cyframi. Na przykład -1 lub 0xabc lub +43 będzie porównywać numerycznie, ale 09 nie będzie (09 jest nieprawidłowy ósemkowy). –

+1

@PaulH - Absolutnie poprawne, ale nie chciałem odwracać uwagi od centralnego punktu pytań i odpowiedzi dotyczących dużych liczb. Z trudem wymyśliłem zwięzłe wyjaśnienie, które nie przytłoczyłoby punktu o tym, że duże liczby traktowane są jako maksymalna dopuszczalna liczba całkowita. Więcej informacji można znaleźć na stronie [Zasady interpretowania numerów CMD.EXE] (http://www.dostips.com/forum/viewtopic.php?t=3758) – dbenham

Powiązane problemy