java 数字操作中的陷阱

代码片段一:

byte i;
  for (i = Byte. MIN_VALUE ; i < Byte. MAX_VALUE; i++) {
         if (i == 0x99) {
            System. out.println( "got it" );
         }
  }

首先这段代码从执行到结束都不会输出 "get it" ,也就是说 i == 0x99 这个判断一直都是返回false。byte是8位,按理说,值的范围是 0xff ~ 0x7e (最高位是符号位)。肯定存在一个值和0x99 相等才对呀。为什么知道执行结束也没有找到呢?
问题就出在 条件语句上:i == 0x99 !!

byte 对于 int 类型来说就是窄类型,同样的int类型对于byte类型来说就是宽类型。这很好理解,byte 8个位,int 32个位,当然int 比 byte 要宽啦。
java中数字不加任何修饰时默认就是int类型的,所以条件语句中0x99 就是int类型的。而宽类型和窄类型在做比较操作时,会隐式的将窄类型符号扩展为宽类型。也就是窄类型进行算术右移(>>),知道达到宽类型的宽度为止。
上面例子中,i == 0x99 这个操作实际上分为了两步,第一步将 i 算术右移 24 位(8+24 = 32)。第二步,将 i 和 0x99 进行比较。

首先我们来看一下 0x99 如果完整了写应该是 0x00000099 。(32位)。对于byte 来说 其对应的 0x99 本身是小于0的,符号位为1。算术右移24位后,完整的16进制表示是0xffffff99。这两个相比较当然不一样了。其他的就不用说了。

那么上面的情况应该如何避免呢?

方案一 :
byte i;
 for (i = Byte. MIN_VALUE; i < Byte.MAX_VALUE; i++) {
       if (i == ( byte) 0x99) {
          System. out.println( "got it");
       }
  }
这种方式就是将0x99强转成byte类型,这样在比较时 i 就不会再隐式的进行符号位扩展。

方案二:

byte i;
 for (i = Byte. MIN_VALUE ; i < Byte. MAX_VALUE; i++) {
       if ((i & 0xff) == 0x99) {
          System. out.println( "got it" );
       }
  }

这种方式是用掩码 0xff 将 只取 i 的最低有效字节,这样和0x99 就不会受到符号位扩展的影响了。

代码片段二:
long v1 = Integer. MAX_VALUE * 2;
long v2 = (long)Integer. MAX_VALUE * 2;

执行后发现,v1 结果是 -2 , v2 是意料中的2倍计算。
这两个有什么区别吗?
先看v1的执行过程:
1、计算Integer.MAX_VALUE * 2 ,这是Integer 类型之间的运算,结果肯定会溢出,得到-2.
2、向v1赋值,并执行符号扩展,最终结果是-2.

再看v2的执行过程:
1、先将Integer强转为long,执行符号扩展。将强转后的值称为temp(此时的temp数值上和转之前是一样的,只是位不同而已)
2、计算temp * 2。 因为temp 是long类型,所以不会导致溢出。计算后的结果也是long类型。

3、将计算结果赋值给v2。

出一道题:

public static boolean isOverFlow(int x, int y) {
            long pll = x * y;
            return pll != (int ) pll;
}


这个函数用于判断两个int类型相乘,是否会溢出。但是这个方法有一个bug,总是返回false,也就是没有溢出。先别往下看。试试看你能找的到吗?答案在最后揭晓。


总结:
1、java中的数字类型都是有符号的,没有无符号类型。最高位是符号位。
2、窄类型向宽类型转换时执行的是符号扩展。
3、窄类型和宽类型混合操作时,会先将窄类型隐式的符号扩展为宽类型,再执行计算。
4、将窄类型赋给宽类型时,隐式的会将执行符号扩展操作。
5、将宽类型向窄类型转换时,会截断多出的高位。例如将long 类型数字强转为 int类型时,只会保留long类型的低32位。

答案:(应该把 long pll = x * y 改成 long pll = (long)x * y 。原因见代码片段二的解释)

转载于:https://my.oschina.net/clopopo/blog/132025

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值