File batch di Windows in caso di errore IF – Come può 30000000000000 uguale a 40000000000?

IF dà la risposta sbagliata quando provo a confrontare 2 numeri grandi.

Ad esempio, questo semplice file batch

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

produce

 30000000000000 is equal to 40000000000 

Cosa sta succedendo e come posso risolvere questo problema?

Se entrambi i lati di un confronto IF sono composti rigorosamente da cifre decimali, IF interpreterà entrambi i lati come numeri. Questo è ciò che consente a IF di determinare correttamente che 10 è maggiore di 9. Se si dispone di caratteri non numerici, IF esegue un confronto tra stringhe. Ad esempio, “10” è inferiore a “9” perché le virgolette non sono cifre e 1 è inferiore a 9.

Il motivo per cui il confronto nella domanda ha esito negativo è perché CMD.EXE non può elaborare numeri maggiori di 2147483647. Una strana stranezza di progettazione in IF considera qualsiasi numero maggiore di 2147483647 come uguale a 2147483647.

Se si desidera eseguire un confronto tra stringhe di numeri grandi, la soluzione è semplice. Devi solo aggiungere 1 o più caratteri non numerici su entrambi i lati della condizione. Il seguente script:

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

produce il risultato corretto del confronto delle stringhe

 "30000000000000" is less than "40000000000" 

Ma nella maggior parte dei casi, questo non è ciò che si desidera.

Se vuoi fare un confronto numerico, il processo è un po ‘più complicato. È necessario convertire il numero in una stringa che verrà ordinata correttamente come un numero. Ciò si ottiene prefissando la stringa numerica con zeri in modo che entrambe le stringhe numeriche abbiano la stessa larghezza. La soluzione più semplice è determinare il numero massimo di cifre che è necessario supportare, diciamo 15 per questo esempio. Quindi si prefissa ogni valore con 15 zeri e quindi si conservano solo i 15 caratteri più a destra utilizzando un’operazione di sottostringa. Devi anche aggiungere una non-cifra su entrambi i lati come prima – di nuovo le virgolette funzionano bene.

Questo script –

 @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 

produce –

 030000000000000 is greater than 000040000000000 

Nota che il prefisso di sinistra con gli spazi funziona altrettanto bene degli zeri.

È ansible rimuovere in seguito gli zeri iniziali ogni volta che si desidera utilizzare il seguente (o adattarsi per rimuovere gli spazi iniziali)

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

Normalmente non trattiamo grandi numeri nei file batch. Ma possono facilmente emergere se guardiamo lo spazio libero su un disco rigido. Le unità disco di Terabyte sono ora relativamente poco costose. Questo è il modo in cui mi sono imbattuto per la prima volta nel confronto di grandi numeri su https://stackoverflow.com/a/9099542/1012053

Ho scelto di supportare 15 cifre nel mio esempio perché ciò equivale a quasi 999 terabyte. Immagino che ci vorrà un po ‘prima di dover gestire unità disco più grandi di così. (Ma chi lo sa!)

EDIT – La mia descrizione di come IF analizza i numeri è intenzionalmente eccessivamente semplicistica. IF supporta effettivamente i numeri negativi, così come la notazione esadecimale e ottale. Vedere Regole per come CMD.EXE analizza i numeri per una spiegazione molto più approfondita.