在批处理文件中进行32位有符号加法,出现“无效数字”错误

3
在下面的脚本中,作为输出的一部分,我得到: =-2147483648+312704 无效数字。数字仅限于32位的精度。
这显然在有符号的32位数字范围内(我还尝试了2147483648+312704,认为只支持无符号32位),但是得到了相同的错误。
奇怪的是,这个脚本在WindowsXP上运行得很好,但是当我在Windows7上尝试运行它时,就会出现上述错误。
批处理为什么不执行此操作呢?
为了方便起见,以下是代码...
setlocal EnableDelayedExpansion

@set TEMPDIR="%CD%\temp739123834543"
@set ADDR=-2147483648
@set HEXADDR=
@set /a ZIPSLEEP_MS=0

@del frontScreenImages.c
@del frontScreen.zip
@mkdir %TEMPDIR%

for %%f in (*.png) do (

    echo %%~nf
    @call toHex ADDR HEXADDR
    echo "ADDR:!HEXADDR!"
    convertFrontScreenPng.exe %%~nf.png frontScreenImages.c transpose append "0x!HEXADDR!" "__xdata_rom" "#pragma required="
rem    set BINFILE=%TEMPDIR%\a!HEXADDR!_%%~nf.bin
    set BINFILE=%TEMPDIR%\a80000000_frontScreenImages.bin
    echo "BIN:!BINFILE!"
    convertFrontScreenPng.exe %%~nf.png !BINFILE! transpose append
    for %%A in (!BINFILE!) do set /a ADDR=%ADDR%+%%~zA
    set /a ZIPSLEEP_MS=!ZIPSLEEP_MS! + 500
)

@echo Set objArgs = WScript.Arguments > _zipIt.vbs
@echo InputFolder = objArgs(0) >> _zipIt.vbs
@echo ZipFile = objArgs(1) >> _zipIt.vbs
@echo CreateObject("Scripting.FileSystemObject").CreateTextFile(ZipFile, True).Write "PK" ^& Chr(5) ^& Chr(6) ^& String(18, vbNullChar) >> _zipIt.vbs
@echo Set objShell = CreateObject("Shell.Application") >> _zipIt.vbs
@echo Set source = objShell.NameSpace(InputFolder).Items >> _zipIt.vbs
@echo objShell.NameSpace(ZipFile).CopyHere(source) >> _zipIt.vbs
@echo wScript.Sleep !ZIPSLEEP_MS! >> _zipIt.vbs

@CScript  _zipIt.vbs  %TEMPDIR%  "%CD%\frontScreen.zip"

@del   _zipIt.vbs
@rmdir %TEMPDIR% /s /q
sleep 3

1
尝试使用 set /a ADDR+=%%~zA,这可以防止命令行解析器使 MIN_INT 值无效化。出于某种奇怪的原因,Windows 7 的 set /a 支持从 -21474836482147483647 的值,但在等式中只允许从 -21474836472147483647 的值。 - David Ruhmann
2
CMD.EXE解析数字的规则。 - Endoro
太棒了!这解决了我的问题。我会标记为已回答。 - brycel
也感谢提供有关数字解析的链接。我曾经搜索过,但显然是在错误的地方。 - brycel
1个回答

2
我重复一下David RuhmannEndoro的答案,并且加上一些额外的信息。
CMD.EXE解析数字的规则中解释了为什么在Windows Vista、Windows 7,以及可能也包括Windows 8中,值-2147483648会导致无效的数字错误消息,而在Windows XP中没有问题。这是因为cmd.exe源代码中的字符串转有符号/无符号整数的代码编写得很糟糕,因此对于最小的32位有符号整数数字不起作用。
解决这个批处理文件的一个方法是使用:
set /a ADDR+=%%~zA

替代

set /a ADDR=%ADDR%+%%~zA

由于这会导致命令行。
set /a ADDR+=312704

替代

set /a ADDR=-2147483648+312704

但这不是唯一的解决方案。
@echo off
set VALUE1=-2147483648
set VALUE2=312704
set /a RESULT=%VALUE1%+%VALUE2%
echo Result of %VALUE1% + %VALUE2% is %RESULT%

导致在 Windows 7 和 Vista 上执行时出现错误消息。
另一个解决方案是大多数 C/C++ 的 limits.h 包含的 INT_MIN 预处理器宏,以避免在编码不良的预处理器上出现相同的问题:将最小有符号 32 位整数的定义作为表达式而不是固定值。
#define INT_MIN  (-2147483647-1)
#define LONG_MIN (-2147483647L-1L)

这个解决方法适用于上面提到的小批处理文件。
@echo off
set VALUE1=(-2147483647-1)
set VALUE2=312704
set /a RESULT=%VALUE1%+%VALUE2%
echo Result of %VALUE1% + %VALUE2% is %RESULT%

现在,即使在Windows 7和Vista上执行批处理文件也没有问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接