Java> 有趣的byte及位运算

byte存储范围,表示范围

我们知道byte代表1个字节,数据存储的范围:[0x00, 0xFF]。byte变量表示值的范围:[-128, 127]。

下面这段代码并不是输出128,而是输出-128,因为byte变量表示值的范围为[-128, 127]。128刚刚好超出上界127有1单位,溢出为-128。
300对应byte值也不是300,而是按byte长度(1字节)截取存储内容后的值。
可以看到,300 = 0b100101100,截取1byte长度后,值为0b00101100,也就有 b = 0b00101100 => b = 44

byte b = (byte) 128;
System.out.println(b); // 输出-128

byte b = (byte) 300; // 300 = 0b100101100, b = 0b101100
System.out.println(b); // 输出44

byte位运算

还有一种比较有意思的现象,就是byte位运算,中间过程是否进行类型转换,也会影响结果。
请看下面的代码,分别打印出什么?(i1, i2):

byte a = (byte)0xFF;
int i1 = 0xFF & a;
int i2 = a;

System.out.println(i1); // 打印255
System.out.println(i2); // 打印-1

运行程序,发现打印i1 为255,打印i2为-1,同样的byte类型变量a = 0xFF,如果直接转换为int类型,值为-1;如果与0xFF先进行按位与运算,值为255。
中间只是对a和0xFF进行了与运算,而a本身宽1字节,但打印结果却不同,这是为什么呢?
 
猜测是不是跟运算过程中的类型转换有关。下面先做验证:

  1. 全部显式转换为byte类型,再 &;
  2. 全部显式转换为int类型,再&;
byte a = (byte)0xFF;
int i1 = 0xFF & a;
int i2 = a;
int i3 = (byte)0xFF & (byte)a;
int i4 = (int)0xFF & (int)a;

System.out.println(i1); // 打印255
System.out.println(i2); // 打印-1
System.out.println(i3); // 打印-1
System.out.println(i4); // 打印255

根据打印结果,可以得出结论:

1. byte型数据跟int型数据进行运算时,会先默认转换为int类型。(类型提升)
2. byte -> int直接类型转换,并不能改变数的符号。

 
byte型位运算为何结果不同于int型位运算?
int和byte都可以表示负数,为什么转换为int进行 & ,与转换为byte进行&,为何结果不一样?
由结论2,知因为int i4 = (int)0xFF & (int)a;中的类型转换(int)并不会改变符号,那么是什么时候改变的呢? 只可能是与0xFF进行&时,改变了a的符号。因为结果是255,但byte a = -1,而i2 = a值也是-1。
详细分析知,0xFF是1byte,而int是2byte。
由于a是int类型,4byte存储,实际a存储值 = 补码(a) = 反码(-a) + 1 = 反码(1) + 1 = ~0b0000 0000 0000 0000 0000 0000 0000 0001 + 1 = 0b1111 1111 1111 1111 1111 1111 1111 1111 = 0xFFFF FFFF
可知,

int i4 = 0xFF & (int)a 
   = 0xFF & -1
   = 0xFF & 0xFFFF FFFF 
   = 0xFF,符号位为0,是正数 
=> i4 = 255

不妨将0xFF改成成0xFFFFFFFF,来验证上面的猜测:

byte a = (byte)0xFF;
...
int i4 = (int)0xFF & (int)a;
int i5 = 0xFFFFFFFF & a;

...
System.out.println(i4); // 打印255
System.out.println(i5); // 输出-1

可以得到i5 = -1,符合猜测预期。 因此,需要十分注意类型转换过程中值的变化,还有位宽的变化,特别是涉及到负数,因为计算机中存储的是补码,就会涉及到符号位。

原文地址:https://www.cnblogs.com/fortunely/p/14277050.html