Board logo

标题: [数值计算] [讨论]批处理if命令数值比较何时发生溢出错误 [打印本页]

作者: neorobin    时间: 2010-4-17 17:45     标题: [讨论]批处理if命令数值比较何时发生溢出错误

WinXP 环境下, cmd 所能处理的数值范围是 [-2^31,  2^31-1], 之前认为 运算结果 超出此范围才发生溢出错误, 而在此范围内的 数值比较 不会发生溢出错误, 但是其实不然.  当两个数都在这个范围内, 但它们的差值的绝对值 大于或等于 2^31-1, 仍将发生溢出错误. 详见下面代码及运行结果:
  1. @echo off
  2. (for /l %%i in (-2147483648,1,-2147483638) do (
  3.   for  /l %%j in (-10,1,10) do (
  4.     if %%j gtr %%i (echo %%j>%%i)   else (echo %%j<=%%i)
  5.     if %%j geq %%i (echo %%j>=%%i) else (echo %%j<=%%i)
  6.     if %%j equ %%i (echo %%j==%%i) else (echo %%j≠%%i)
  7.     if %%j leq %%i (echo %%j<=%%i) else (echo %%j>%%i)
  8.     if %%j lss %%i (echo %%j<%%i)   else (echo %%j>=%%i)
  9. )))>t.txt
  10. type t.txt | findstr "<"&&pause
  11. :bigPlus
  12. echo 以下是大数接近 2^^31-1 的情况
  13. for  /l %%j in (2147483637,1,2147483647) do (
  14.   for /l %%i in (-10,1,10) do (
  15.     if %%j gtr %%i (echo %%j>%%i)   else (echo %%j<=%%i)
  16.     if %%j geq %%i (echo %%j>=%%i) else (echo %%j<=%%i)
  17.     if %%j equ %%i (echo %%j==%%i) else (echo %%j≠%%i)
  18.     if %%j leq %%i (echo %%j<=%%i) else (echo %%j>%%i)
  19.     if %%j lss %%i (echo %%j<%%i)   else (echo %%j>=%%i)
  20.   )>t.txt
  21.   type t.txt | findstr "<"&&pause
  22. )
复制代码
在小的数接近 -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 中
  1. begin
  2.   if 2147483647 > -2147483647 then Write('2147483647>-2147483647') else Write('2147483647<=-2147483647');
  3.   Readln;
  4. end.
复制代码
可正常输出结果:
2147483647>-2147483647

但试图
  1. 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