Java-随便学学-整数溢出问题

0.开头

这次的问题基于上一篇的变量值互换程序,也就是不引入第三个变量的互换。

1.整数溢出究竟是什么?

  • 首先,以int为例,我们知道int由32位二进制数据表示,其中最高位表示正负号,1表示负数,0表示正数,所以int的取值范围是[-2 ^ 31 , 2 ^ 31-1].并且其中1000 0000 0000 0000 0000 0000 0000 0000表示的是-2 ^ 31,全0则表示0。
  • 然后,设想一种情况,两个int类型相加超过了int所能表示的范围会发生什么?
public class Temp {
    public static void main(String[]args){
        int a = 2147483647;
        int b = a+1;//a+1理论上等于2147483648,也就是2的31次方
        int c = a+2;//a+2理论上等于2147483649,也就是2的31次方+1
        //实际输出
        System.out.println("b="+b);
        System.out.println("c="+c);
    }
}
b=-2147483648
c=-2147483647

a+1理论上等于2147483648,也就是2的31次方;
a+2理论上等于2147483649,也就是2的31次方+1;
实际输出却变成了负数,难道是计算机出错了吗?
实际上,计算没有错,我们可以看一下a,b,c对应的二进制形式;

System.out.println("a的二进制形式:"+Integer.toBinaryString(a));
System.out.println("b的二进制形式:"+Integer.toBinaryString(b));
System.out.println("c的二进制形式:"+Integer.toBinaryString(c));
a的二进制形式:1111111111111111111111111111111
b的二进制形式:10000000000000000000000000000000
c的二进制形式:10000000000000000000000000000001

根据二进制的表现,我们可以看出,其实计算机并没有算错,只不过在int表示的数字当中,首位被当做了符号(正负号),所以计算结果才会出现问题。

2.为什么计算溢出了没有影响数值转化结果?

我们看一下这个变量数值互换程序:

public class ValueExchange {
    public static void  main(String[]args){
        int a1 = 2147483646;
        int b1 = 2147483647;
        a1 = a1+b1;
        System.out.println("a1中间值为:"+a1);
        b1 = a1-b1;
        a1 = a1-b1;
        System.out.println("a1="+a1);
        System.out.println("b1="+b1);
    }
}
a1中间值为:-3
a1=2147483647
b1=2147483646

这个程序显然在a1=a1+b1;这行出现了溢出问题;那么问题来了,为什么它没有造成最后的互换结果出错呢?
之前我们提到,在实际计算的时候,计算机并没有出错,只不过是首位被当成了符号而已。我们打印一下a1的中间值:

a1中间值二进制形式为:11111111111111111111111111111101

这个数值如果用long类型表示,实际的值:

System.out.println("a1中间值二进制形式转化为long类型:"+Long.parseLong(Integer.toBinaryString(a1),2));

结果是

a1中间值二进制形式转化为long类型:4294967293

正好是a1+b1(2147483646+2147483647)的理论值,所以这里我们说计算机的计算并没有出错。
下面的两行给b1和a1重新赋值的代码一定也是这种情况,而且在计算之后,刚好他们的值在int能够表示的范围里,所以才导致最终结果没有问题。

此处的小结论:在变量值互换的方法中,如果计算过程没有问题,而最后的结果极有可能还是正确的。

3.其他情况?

你们或许注意到了,我在上面的结论中,并没有肯定的表示计算一定不会出现问题。我们将上面的程序进行修改:
将a1和b1的值都变成负数,也就是二进制形式的首位都变成了1。
为什么要这样做修改?是因为之前讨论的情况当中,两个正数int值相加的时候,结果不可能出现33位,也就是首位都是0的两个数,是不会因为最高位进位导致进一步溢出的。所以既然计算是不会出问题的,那么这样修改,是为了让结果进一步溢出,让int直接丢失掉真实的最高位。

public class Temp {
    public static void main(String[]args){
        int a1 = -2147483646;
        int b1 = -2147483647;
        a1 = a1+b1;
        System.out.println("a1中间值为:"+a1);
        System.out.println("a1中间值二进制形式为:"+Integer.toBinaryString(a1));
        System.out.println
                ("a1中间值二进制形式转化为long类型:"+Long.parseLong(Integer.toBinaryString(a1),2));
        b1 = a1-b1;
        a1 = a1-b1;
        System.out.println("a1="+a1);
        System.out.println("b1="+b1);
    }
}

果然,我们发现,a1的形式已经不是a1+b1(-2147483646-2147483647)的理论值了,原因正如我的修改目的所说,最高位(结果33位)丢了。

a1中间值为:3
a1中间值二进制形式为:11
a1中间值二进制形式转化为long类型:3
a1=-2147483647
b1=-2147483646

但是最后的交换结果还是没有问题,这又是为什么呢?真是纳了闷了??
这时,我们发现a1 = a1+b1;这句的分析是没有问题的,也就是结果为3的这一步;
出现问题的地方在下一步b1 = a1-b1;这一步再一次发生了溢出这里的溢出使得最后结果又转了回来,转到了正确答案上!惊了!

这样说的话,这种方案即使溢出也不用管咯?如果你只是希望交换两个变量的值,这个方法就无敌了?
暂时还没有想通,希望有人指点迷津。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值