首页 > 其他 > 详细

由一段会被人嘲笑的代码想到的

时间:2014-04-23 12:55:28      阅读:775      评论:0      收藏:0      [点我收藏+]

这是一个关于数据溢出的问题,如果你对这个知识点感兴趣请继续往下看。引入问题之前我们先温习一下溢出的概念:一个算数运算溢出,是指完整的整数结果不能放到数据类型的字长限制中去。

问题:写出一个具有如下原型的函数,如果参数x 和 y 相加不会产生溢出,这个函数就返回 1.

bubuko.com,布布扣
int tadd_ok(int x,int y);
bubuko.com,布布扣

错误的解决办法:

bubuko.com,布布扣
1 /* WARNING: This code is buggy. */
2 boolean tadd_ok(int x,int y){
3     int sum=x+y;
4     return (sum-x == y) && (sum-y == x);
5 }
bubuko.com,布布扣

这段代码的思路是,如果发生了溢出 (x+y)-y 的结果就不会等于x,且 (x+y)-x 的结果也不会等于y.但实际测试结果是,函数 tadd_ok() 的返回值总是 true。为什么会产生这样的结果呢?下面先来看另一段代码:

bubuko.com,布布扣
public class demo01 {
    public static void main(String[] args) {
        int sum=2147483647+3;       //IntMax:2147483647
        System.out.println("sum="+sum);
        System.out.println("sum-3="+(sum-3));
    }

}
//output:
sum = -2147483646
sum-3 = 2147483647    
bubuko.com,布布扣

  从上面代码中看到,2147483647+3 后发生了溢出,但是溢出后的值减3后又得到了原来的值2147483647。这看起来似乎不合常理,因为发生溢出后,超过数据类型字长限制的部分会被丢弃,这样运算的结果就不会是我们预期的值,而且逆运算之后也更加不会得到原来的初值,但实际情况却不是这样的。

  为了更直观地解释这种“诡异”的现象,我们假设机器字长为4位,x=9 和 x=12 的位表示分别为 [1001] 和 [1100],它们的和是21 (5位数字表示为 [10101]),但是机器位只有4位,于是最高位会被丢弃,我们就得到 [0101],也就是十进制的5.我们看到,在 x+y 的运算过程中发生了溢出,导致运算结果5与预期值21不符,但是模拟上面的程序,再进行逆运算会发生什么呢?即运算结果5减去9,如果5-9的结果等于12就说明与上面的程序运行结果相匹配,我们不妨仔细看看运算过程:

      5-9 = 5 + (-9) = [0101] + [0111] = [1100] =12.

  上面的计算过程可能还不够清晰,‘-9‘的补码为什么是 [0111],[0111] 不是 7 的补码吗?难道说 -9 =7 吗?在这里给出两种解释方式:(这仅仅是我认为正确的思路,标准答案是什么还望高手不吝赐教)

    第一种解释:把 [0111] 并不是 ‘-9‘ 的补码,而是表达式 (-9) 的4位二进制表示。我们知道,对于任意整数值 x ,计算表达式 -x 和 ~x+1 得到的结果完全一样。利用这个结论,我们可以得到 ‘-9‘ 的位表示推理过程 [1001] 取反得到 [0110], 加1得 [0111].

    第二种解释:按照计算机中有符号数的存储规则, ‘-9‘ 的补码按照不同位数表示是 [10111] 、[110111]、[1110111]...但是我们已经假设了机器字长为4位,所以必须丢弃超过限定字长的部分,丢弃后得到的补码就是 [0111].这与第一种解释相吻合。 

  这样我们就能得出一个结论:即使加法产生了溢出,进行逆运算后的值依然等于参与运算的初值,即无论加法是否溢出,而(x+y)-y 总是会求值得到 x

  好了,我们已经利用这个简单的例子说明这个运算时可逆的,也就证明了上面红体字部分的正确性,也就说明给出的解决办法是错误的。

正确的解决办法:

bubuko.com,布布扣
1 int tadd_ok(int x,int y){
2     int sum = x+y;
3     int neg_over = x < 0 && y < 0 && sum >= 0;
4     int pos_over = x >=0 && y >= 0 && sum < 0;
5     return !neg_over && !pos_over;
6 }
bubuko.com,布布扣

  后记:数据的溢出往往会造成程序的不可靠性,所以在程序中应该考虑到这个问题所可能产生的后果,并采取必要的措施来处理这种情况。当然,这样做的前提是你已经对溢出有了比较深入的理解。

  

  注:本文整理自《深入理解计算机系统》.

由一段会被人嘲笑的代码想到的,布布扣,bubuko.com

由一段会被人嘲笑的代码想到的

原文:http://www.cnblogs.com/wd-eco/p/3682009.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!