Page 3 of 3

### Re: Rules for how CMD.EXE parses numbers

Posted: 24 May 2014 01:36
On windows 8 try set -2147483648 direclty using decimal notation (instead 0x80000000) cause the overflow message:

Code: Select all

``set /a -2147483648Invalid number.  Numbers are limited to 32-bits of precision.``

but you can cause the overflow for set this number using this:

Code: Select all

``set /a 1+2147483647-2147483648``

Also:

Code: Select all

``set /a "(1<<31)"-2147483648``

### Re: Rules for how CMD.EXE parses numbers

Posted: 24 May 2014 07:03
Yes, that was one of the quirks that led me to investigate and write this thread.

The inability to directly input -2147483648 is strictly a limitation of the decimal number parser. It has nothing to do with the inherit limits of SET /A math capabilities.

Dave Benham

### Re: Rules for how CMD.EXE parses numbers

Posted: 24 May 2014 10:24
Additional info about IF statement:

When a difference beetween the operands is greater than MAX int-type (2147483647 + 1),
operand LSS always return 'FALSE'
operand LEQ always return 'FALSE'
operand GTR always return 'TRUE'
operand GEQ always return 'TRUE'

Code: Select all

``@echo offSetLocal EnableExtensionsset MAX=2147483640For %%o in (LSS LEQ GTR GEQ) do (  For /L %%n in (-9 1 -6) do (    set /p =%%n %%o %MAX% ? <NUL    call :# %%n %%o %MAX% & rem 'IF' statement don't support delayed method for operator  )  echo -------------------------)pause & exit /B:# [_in_operand1] [_in_operator] [_in_operand2]if %~1 %~2 %~3 (echo true  +) else (echo FALSE -)``

Result:
-9 LSS 2147483640 ? FALSE -
-8 LSS 2147483640 ? true +
-7 LSS 2147483640 ? true +
-6 LSS 2147483640 ? true +
-------------------------
-9 LEQ 2147483640 ? FALSE -
-8 LEQ 2147483640 ? true +
-7 LEQ 2147483640 ? true +
-6 LEQ 2147483640 ? true +
-------------------------
-9 GTR 2147483640 ? true +
-8 GTR 2147483640 ? FALSE -
-7 GTR 2147483640 ? FALSE -
-6 GTR 2147483640 ? FALSE -
-------------------------
-9 GEQ 2147483640 ? true +
-8 GEQ 2147483640 ? FALSE -
-7 GEQ 2147483640 ? FALSE -
-6 GEQ 2147483640 ? FALSE -

EQU and NEQ works well here.

### Re: Rules for how CMD.EXE parses numbers

Posted: 24 May 2014 10:54
Dragokas wrote:When a difference beetween the operands is greater than MAX int-type (2147483647 + 1),
operand LSS always return 'FALSE'
operand LEQ always return 'FALSE'
operand GTR always return 'TRUE'
operand GEQ always return 'TRUE'

OMG - That is so terrible

A great discovery, but such a horrendous bug/design flaw by MS is inexcusable.

Presumably MS implemented the comparison as a subtraction, and then they look for either negative, zero, or positive. Math overflow would cause the bug.

Dave Benham

### Re: Rules for how CMD.EXE parses numbers

Posted: 24 May 2014 14:28

This means that any math-like batch file should be revised... ... ... .

For example the great MD5 Algorithm (uses IF %~Z1 GTR 268435455, and other inequations):

Code: Select all

``IF -1879048192 GTR 268435455 (echo yes) else (echo Oh No)``

Or the Multi-process Advanced Encryption Standard (AES) (if !d! geq 16, ...):

Code: Select all

``if -2147483647 GEQ 16 echo a``

... ... (or all the other great math tools)

penpen

### Re: Rules for how CMD.EXE parses numbers

Posted: 25 May 2014 04:29
Seems that this implemented like this, using a substractions as dbenham says:

lss = ( n1 - n2 ) < 0
leq = ( n1 - n2 ) <= 0
gtr = ( n1 - n2 ) > 0
geq = ( n1 - n2 ) >= 0
neq = ( n1 - n2 ) != 0
equ = ( n1 - n2 ) == 0

what bad, the substraction is all unnecessary, and is the bug.
But only operators distinct than equ and neq, and if are on different sign are affected.

### Re: Rules for how CMD.EXE parses numbers

Posted: 26 May 2014 11:55
Below is an IF macro that eliminates the bug. Unfortunately it is >150 times slower

Usage is simple:

Code: Select all

``(%if% val1 condition val2) && (TrueBlock) || (FalseBlock)``

If the last statment in the TrueBlock might raise an error, then an additional command must be added that always succeeds. Otherwise the FalseBlock might fire after the TrueBlock. I like to use (CALL ) with a trailing space.

Code: Select all

``(%if% -9 lss 2147483640) && (  echo TRUE  copy someFileThatMightNotExist newLocation  (call )) || (  echo FALSE)``

Here is the macro with test cases

Code: Select all

``@echo offsetlocal:: ----------- Define the IF macro ----------------set ^"LF=^^"set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"set if=for %%# in (1 2) do if %%#==2 (setlocal enableDelayedExpansion^&for /f "tokens=1-3 delims= " %%1 in ("!args!") do (%\n%  set /a test=0, rtn=1%\n%  if %%1 lss 0 if %%3 gtr 0 set test=-1%\n%  if %%1 gtr 0 if %%3 lss 0 set test=1%\n%  if /i %%2 equ lss (%\n%    if !test! equ -1 set rtn=0%\n%    if !test! equ 0 if %%1 lss %%3 set rtn=0%\n%  ) else if /i %%2 equ leq (%\n%    if !test! equ -1 set rtn=0%\n%    if !test! equ 0 if %%1 leq %%3 set rtn=0%\n%  ) else if /i %%2 equ gtr (%\n%    if !test! equ 1 set rtn=0%\n%    if !test! equ 0 if %%1 gtr %%3 set rtn=0%\n%  ) else if /i %%2 equ geq (%\n%    if !test! equ 1 set rtn=0%\n%    if !test! equ 0 if %%1 geq %%3 set rtn=0%\n%  ) else if /i %%2 equ equ (%\n%    if %%1 equ %%3 set rtn=0%\n%  ) else if /i %%2 equ neq (%\n%    if %%1 neq %%3 set rtn=0%\n%  )%\n%  for %%N in (!rtn!) do (%\n%    endlocal^&endlocal%\n%    if %%N equ 0 (call ) else (call)%\n%  )%\n%)) else setlocal^&set args=:: ------------ Test the IF macro ------------------for %%S in (  "-9 2147483640"  "-8 2147483640"  "8 -2147483640"  "7 -2147483640"  "9 2147483640"  "2147483640 8"  "-9 -2147483640"  "-8 -2147483640"  "-9 -9") do for /f "tokens=1,2" %%A in ("%%~S") do (  for %%T in (lss leq gtr geq equ neq) do (    (%if% %%A %%T %%B) && (echo macro:  %%A %%T %%B TRUE) || (echo macro:  %%A %%T %%B FALSE)    call :test %%A %%T %%B    echo(  )  echo()exit /b:testif %1 %2 %3 (echo normal: %1 %2 %3 TRUE) else (echo normal: %1 %2 %3 FALSE)exit /b``

--OUTPUT--

Code: Select all

``macro:  -9 lss 2147483640 TRUEnormal: -9 lss 2147483640 FALSEmacro:  -9 leq 2147483640 TRUEnormal: -9 leq 2147483640 FALSEmacro:  -9 gtr 2147483640 FALSEnormal: -9 gtr 2147483640 TRUEmacro:  -9 geq 2147483640 FALSEnormal: -9 geq 2147483640 TRUEmacro:  -9 equ 2147483640 FALSEnormal: -9 equ 2147483640 FALSEmacro:  -9 neq 2147483640 TRUEnormal: -9 neq 2147483640 TRUEmacro:  -8 lss 2147483640 TRUEnormal: -8 lss 2147483640 TRUEmacro:  -8 leq 2147483640 TRUEnormal: -8 leq 2147483640 TRUEmacro:  -8 gtr 2147483640 FALSEnormal: -8 gtr 2147483640 FALSEmacro:  -8 geq 2147483640 FALSEnormal: -8 geq 2147483640 FALSEmacro:  -8 equ 2147483640 FALSEnormal: -8 equ 2147483640 FALSEmacro:  -8 neq 2147483640 TRUEnormal: -8 neq 2147483640 TRUEmacro:  8 lss -2147483640 FALSEnormal: 8 lss -2147483640 TRUEmacro:  8 leq -2147483640 FALSEnormal: 8 leq -2147483640 TRUEmacro:  8 gtr -2147483640 TRUEnormal: 8 gtr -2147483640 FALSEmacro:  8 geq -2147483640 TRUEnormal: 8 geq -2147483640 FALSEmacro:  8 equ -2147483640 FALSEnormal: 8 equ -2147483640 FALSEmacro:  8 neq -2147483640 TRUEnormal: 8 neq -2147483640 TRUEmacro:  7 lss -2147483640 FALSEnormal: 7 lss -2147483640 FALSEmacro:  7 leq -2147483640 FALSEnormal: 7 leq -2147483640 FALSEmacro:  7 gtr -2147483640 TRUEnormal: 7 gtr -2147483640 TRUEmacro:  7 geq -2147483640 TRUEnormal: 7 geq -2147483640 TRUEmacro:  7 equ -2147483640 FALSEnormal: 7 equ -2147483640 FALSEmacro:  7 neq -2147483640 TRUEnormal: 7 neq -2147483640 TRUEmacro:  9 lss 2147483640 TRUEnormal: 9 lss 2147483640 TRUEmacro:  9 leq 2147483640 TRUEnormal: 9 leq 2147483640 TRUEmacro:  9 gtr 2147483640 FALSEnormal: 9 gtr 2147483640 FALSEmacro:  9 geq 2147483640 FALSEnormal: 9 geq 2147483640 FALSEmacro:  9 equ 2147483640 FALSEnormal: 9 equ 2147483640 FALSEmacro:  9 neq 2147483640 TRUEnormal: 9 neq 2147483640 TRUEmacro:  2147483640 lss 8 FALSEnormal: 2147483640 lss 8 FALSEmacro:  2147483640 leq 8 FALSEnormal: 2147483640 leq 8 FALSEmacro:  2147483640 gtr 8 TRUEnormal: 2147483640 gtr 8 TRUEmacro:  2147483640 geq 8 TRUEnormal: 2147483640 geq 8 TRUEmacro:  2147483640 equ 8 FALSEnormal: 2147483640 equ 8 FALSEmacro:  2147483640 neq 8 TRUEnormal: 2147483640 neq 8 TRUEmacro:  -9 lss -2147483640 FALSEnormal: -9 lss -2147483640 FALSEmacro:  -9 leq -2147483640 FALSEnormal: -9 leq -2147483640 FALSEmacro:  -9 gtr -2147483640 TRUEnormal: -9 gtr -2147483640 TRUEmacro:  -9 geq -2147483640 TRUEnormal: -9 geq -2147483640 TRUEmacro:  -9 equ -2147483640 FALSEnormal: -9 equ -2147483640 FALSEmacro:  -9 neq -2147483640 TRUEnormal: -9 neq -2147483640 TRUEmacro:  -8 lss -2147483640 FALSEnormal: -8 lss -2147483640 FALSEmacro:  -8 leq -2147483640 FALSEnormal: -8 leq -2147483640 FALSEmacro:  -8 gtr -2147483640 TRUEnormal: -8 gtr -2147483640 TRUEmacro:  -8 geq -2147483640 TRUEnormal: -8 geq -2147483640 TRUEmacro:  -8 equ -2147483640 FALSEnormal: -8 equ -2147483640 FALSEmacro:  -8 neq -2147483640 TRUEnormal: -8 neq -2147483640 TRUEmacro:  -9 lss -9 FALSEnormal: -9 lss -9 FALSEmacro:  -9 leq -9 TRUEnormal: -9 leq -9 TRUEmacro:  -9 gtr -9 FALSEnormal: -9 gtr -9 FALSEmacro:  -9 geq -9 TRUEnormal: -9 geq -9 TRUEmacro:  -9 equ -9 TRUEnormal: -9 equ -9 TRUEmacro:  -9 neq -9 FALSEnormal: -9 neq -9 FALSE``

Dave Benham

### Re: Rules for how CMD.EXE parses numbers

Posted: 26 May 2014 12:42
This is other bug, on set /a

Hello. As you know in set /a you can do three types of expansion:

normal expansion : %var%
delayed expansion: !var!
name expansion: var

The problem with the name expansion is that if we have a negative number stored as hexadecimal notation in a variable called var, and we use set /a var that expansion will convert that hexadecimal negative number to the max positive number: 2147483647

Check this example:

Code: Select all

``@Echo OffSetLocal EnableExtensions EnableDelayedExpansionRem Set a negative number in variable var: -5Cmd /C Exit /B -5Echo The hexadecimal representation of -5 is: 0x!=ExitCode!Rem -5 = 0xFFFFFFFBSet "var=0xFFFFFFFB"Echo Look the difference in the name expansion.Echo It turn the negative number in hexadecimal notation to 2147483647Set /A "normal_exp=%var%"Set /A "delayed_exp=!var!"Set /A "name_exp=var"Echo Normal Expansion: %normal_exp%Echo Delayed Expansion: %delayed_exp%Echo Name Expansion: %name_exp%PauseGoto :Eof``

It prints:
Normal Expansion: -5
Delayed Expansion: -5
Name Expansion: 2147483647

### Re: Rules for how CMD.EXE parses numbers

Posted: 26 May 2014 13:13
@dbenham: Nice macro!

Sad to say, that i've found another bug (at least on my win xp home 32 bit):

Code: Select all

``Z:\>if 20000000000 lss 0 (echo a) else echo bbZ:\>if 0x80000000 lss 0 (echo a) else echo bbZ:\>if -2147483648 lss 0 (echo a) else echo ba``

penpen

### Re: Rules for how CMD.EXE parses numbers

Posted: 26 May 2014 13:47
penpen wrote:Sad to say, that i've found another bug (at least on my win xp home 32 bit):

Code: Select all

``Z:\>if 20000000000 lss 0 (echo a) else echo bbZ:\>if 0x80000000 lss 0 (echo a) else echo bbZ:\>if -2147483648 lss 0 (echo a) else echo ba``

That behavior has already been described by Liviu within this thread at viewtopic.php?p=24799#p24799. I then updated the original post in this thread to reflect the SET /A variable rule as follows:
dbenham wrote:Variables (all versions)

The rules for parsing un-expanded numeric variables are different. All three numeric notations employ a similar strategy: First ignore any leading negative sign and convert the number into an unsigned binary representation. Then apply any leading negative sign by taking the 2's compliment.

The big difference is that overflow conditions no longer result in an error. Instead the maximum magnitude value is used. A positive overflow becomes 2147483647, and a negative overflow becomes -2147483648.

Undefined variables are treated as zero, and variables that do not contain a valid numeric format are treated as zero.

Dave Benham

### Re: Rules for how CMD.EXE parses numbers

Posted: 17 Mar 2015 10:22
I found that the %~z expansion of for command treat the size (tested only on windows 7 sp1) as text. For example a big file with size greather than the max 32 bit number 2147483647, like 4700000000 is used without the 32 bit limitation.

Code: Select all

``C:\dev>set /a 4700000000Número no válido. Los números están limitados a 32 bits de precisión.``

Code: Select all

``C:\dev>fsutil file createnew BIG 4700000000El archivo C:\dev\BIG está creadoC:\dev>FOR %# IN (BIG) DO @ECHO %~Z#4700000000``