标题: [数值计算] [讨论]批处理if命令数值比较何时发生溢出错误 [打印本页]
作者: neorobin 时间: 2010-4-17 17:45 标题: [讨论]批处理if命令数值比较何时发生溢出错误
WinXP 环境下, cmd 所能处理的数值范围是 [-2^31, 2^31-1], 之前认为 运算结果 超出此范围才发生溢出错误, 而在此范围内的 数值比较 不会发生溢出错误, 但是其实不然. 当两个数都在这个范围内, 但它们的差值的绝对值 大于或等于 2^31-1, 仍将发生溢出错误. 详见下面代码及运行结果:- @echo off
- (for /l %%i in (-2147483648,1,-2147483638) do (
- for /l %%j in (-10,1,10) do (
- if %%j gtr %%i (echo %%j>%%i) else (echo %%j<=%%i)
- if %%j geq %%i (echo %%j>=%%i) else (echo %%j<=%%i)
- if %%j equ %%i (echo %%j==%%i) else (echo %%j≠%%i)
- if %%j leq %%i (echo %%j<=%%i) else (echo %%j>%%i)
- if %%j lss %%i (echo %%j<%%i) else (echo %%j>=%%i)
- )))>t.txt
- type t.txt | findstr "<"&&pause
- :bigPlus
- echo 以下是大数接近 2^^31-1 的情况
- for /l %%j in (2147483637,1,2147483647) do (
- for /l %%i in (-10,1,10) do (
- if %%j gtr %%i (echo %%j>%%i) else (echo %%j<=%%i)
- if %%j geq %%i (echo %%j>=%%i) else (echo %%j<=%%i)
- if %%j equ %%i (echo %%j==%%i) else (echo %%j≠%%i)
- if %%j leq %%i (echo %%j<=%%i) else (echo %%j>%%i)
- if %%j lss %%i (echo %%j<%%i) else (echo %%j>=%%i)
- )>t.txt
- type t.txt | findstr "<"&&pause
- )
复制代码
在小的数接近 -2^31 时, 比较结果会发生溢出错误(差值溢出), 但显示的数值正常.
而在大数接近 2^31-1 时, 发生溢出错误时, 连显示的数值也出现错误 -- 标号 :bigPlus 后面的代码中, %%j 的取值一直是正数, 但显示结果却成了负数).
部分错误结果如下, 要查看完全的结果请运行上面的测试代码 及 扩大测试的范围
0<=-2147483648
0<=-2147483648
0<=-2147483648
0<-2147483648
1<=-2147483648
1<=-2147483648
1<=-2147483648
1<-2147483648
2<=-2147483648
2<=-2147483648
2<=-2147483648
2<-2147483648
.
.
.
1<=-2147483647
...
2<=-2147483646
...
3<=-2147483645
...
10<=-2147483638
10<=-2147483638
10<=-2147483638
10<-2147483638
请按任意键继续. . .
以下是大数接近 2^31-1 的情况
-2147483638<=10
-2147483638<=10
-2147483638<=10
-2147483638<10
请按任意键继续. . .
-2147483637<=10
-2147483637<=10
-2147483637<=10
-2147483637<10
请按任意键继续. . .
[ 本帖最后由 neorobin 于 2010-4-17 18:13 编辑 ]
作者: qzwqzw 时间: 2010-4-17 22:17
还好你的帖子解锁了
就在此回复吧
if的数值比较与其它语言是一样的原理
核心都是减法运算
根据减法的差值判定大小
差值为正就是大于
为负就是小于
为零就是等于
而差值本身也是变量
也需要存储
也存在所有32位的双字值都有的-2^31~2^31-1的限定
因为3 - (-2147483645) 的结果超界
结果本来的正值变成负值(不完全是溢出)
本来的大于就变成了小于了
大正数的溢出问题
实际上是因为for /l本身含有加法运算
而你所设置的下限2147483647
是32位整数的最大值
在加1就成为负值
所以你的for的永远不会超限
成了“死循环”
作者: neorobin 时间: 2010-4-17 22:41 标题: 回复 2楼 的帖子
在高级程序语言中, 两个数值本身没有溢出, 比较时是不会产生溢出错误的, 例如, 在 PASCAL 中- begin
- if 2147483647 > -2147483647 then Write('2147483647>-2147483647') else Write('2147483647<=-2147483647');
- Readln;
- end.
复制代码
可正常输出结果:
但试图- if 2147483647 > -2147483648 then Write('...');
复制代码
却将引起编译报错
关于循环界限, 确实如你所言, 上限改成 2147483646 后, 循环就可以正常终止, 而且也不产生错误的比较结果
[ 本帖最后由 neorobin 于 2010-4-17 23:27 编辑 ]
作者: hanyeguxing 时间: 2010-4-18 00:44
在cmd和批处理中,各个命令的有效范围不一样
对于set/a的来,一般是有效值范围是-2147483648至2147483647
对于于for来说,有效值范围是[-2147483647,2147483646],而且起始值迭上步长超过[-2147483648,2147483647]的范围,都会开始错误循环
[ 本帖最后由 hanyeguxing 于 2010-4-18 00:45 编辑 ]
作者: Demon 时间: 2012-8-13 16:28
http://demon.tw/reverse/cmd-internal-if.html
正如2楼所说,IF在比较两个数大小的时候会用第一个数减去第二个数,用它们的差与0做比较。
当两个数的差大于等于2147483648(0x80000000)时,32位有符号整数的最高位为1,表示负数,比0要小,所以IF认为第一个数小于第二个数。
欢迎光临 批处理之家 (http://bbs.bathome.net/) |
Powered by Discuz! 7.2 |