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


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

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

int tadd_ok(int x,int y);

错误的解决办法:

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 }

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

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    

  从上面代码中看到,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

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

正确的解决办法:

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 }

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

  

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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM