
在Java中,如果我们执行以下操作,则将启动Sign扩展 :
- 扩展原始转换–从一种类型转换为另一种类型,这涉及到增加原始类型的位,例如,将
byte (8 bits)
为int (32 bits)
。 - 按位右移n位
>> n
。
符号扩展名是在保留数字的符号(正号和负号)的同时增加位数的操作。 除一个条件外,Sign扩展名用最高有效位(msb)或最左边的位的原始位填充增加的位-将无符号的char
为其他类型,请向下滚动至3. Java Multicast
进行解释。
本文还通过以下代码解释了著名的Java多播问题。 你能回答吗?
byte b = -1; // twos complement
char c = (char) (b); // byte -> int -> char (widening and narrowing type) (sign extension)
System.out.println((int)c); // char -> int (zero extension), output = 65535
c = (char) (b & 0xff); // byte -> int (sign extension)
System.out.println((int) c); // char -> int (zero extension), output = 255
1. Java Sign扩展–扩展原始转换
例如,我们将一个byte
(8位)转换为int
(32位)。
正数
1.1这是二进制格式的byte 10
10,8位(1个字节)。
0000 1010 = 10
如果我们将byte
(8位)强制转换/加宽为int
(32位,4字节),则额外位的值(1或0)是什么?
# byte, 1 byte, 8 bits
0000 1010 = 10
# int, 4 bytes, 32 bits
???? ???? | ???? ???? | ???? ???? | 0000 1010 = 10
答案是符号扩展。 它取决于“原始”最高有效位(msb)或最左侧的位。 在上面的示例中, byte 10
或0000 1010
的msb为0
,并且符号扩展用零填充所有额外的未知位。
{0}000 1010 , {0} = most significant bit or leftmost bit.
将byte 10
int
为int
,结果仍然为10
。
0000 0000 | 0000 0000 | 0000 0000 | 0000 1010 = 10
byte aByte = 10;
int aByte1 = (int) aByte;
System.out.println(aByte1); // output = 10
负数
1.2对于byte -10
,Java使用二进制补码表示负值。
# two's complement formula
0000 1010 = 10
1111 0101 (invert bits)
1111 0110 (add 1)
1111 0110 = -10
如果我们将byte -10
(8位)强制转换/扩展为int
(32位,4字节),则额外位的值(1或0)是多少?
???? ???? | ???? ???? | ???? ???? | 1111 0110 = -10
对于byte -10
或1111 0110
,最显著位1111 0110
是1
,而符号扩展填充所有的未知位一个。
{1}111 0110 , {1} = most significant bit or leftmost bit.
将byte -10
为int
,结果仍然为-10
。
1111 1111 | 1111 1111 | 1111 1111 | 1111 0110
对于二进制补码(最左边的1为负,0为正)
# two's complement formula
1111 1111 | 1111 1111 | 1111 1111 | 1111 0110 (this is a negative number)
1111 1111 | 1111 1111 | 1111 1111 | 1111 0101 (sub 1)
0000 0000 | 0000 0000 | 0000 0000 | 0000 1010 (invert bits)
0000 0000 | 0000 0000 | 0000 0000 | 0000 1010 = 10
1111 1111 | 1111 1111 | 1111 1111 | 1111 0110 = -10
byte aByte = -10;
int aByte1 = (int) aByte;
System.out.println(aByte1); // output = -10
困难的部分是了解两者的互补 。
2. Java符号扩展–按位右移>>
按位移位运算符>>
(也称为算术右移),它在移位后保留符号(正数或负数)。
例如,这里是整数10。在Java中, int
是32位4字节。
0000 0000 | 0000 0000 | 0000 0000 | 0000 1010
然后我们右移3位10 >> 3
。 对于整数10
或{0=msb}0001010
,最高有效位为零,并且符号扩展用零填充所有未知位。
???0 0000 | 0000 0000 | 0000 0000 | 0000 0001 | 010 >> 3
10 >> 3
的答案是1
。
0000 0000 | 0000 0000 | 0000 0000 | 0000 0001 = 1
System.out.println(10>>3); // output = 1
进一步阅读
Java >>和>>>按位移位运算符 。
3. Java组播-符号扩展和零扩展。
到目前为止,一切看起来都很简单明了。 真正的挑战是多播,这使一切变得复杂。
查看下面的Java多播示例(如果我不记得记错的话,我从一个论坛上找到了此代码片段,其原文来自Java Puzzle丛书)。
byte b = -1; // twos complement
char c = (char) (b); // byte -> int -> char (widening and narrowing type) (sign extension)
System.out.println((int)c); // char -> int (zero extension), output = 65535
c = (char) (b & 0xff); // byte -> int (sign extension)
System.out.println((int) c); // char -> int (zero extension), output = 255
上面的示例涉及多播,从byte
-> int
> char
> int
,二进制补码,加宽原始转换,缩小原始转换,符号扩展,零扩展以及按位屏蔽。 让我们一一分解。
3.1 Java使用二进制补码表示否定。
0000 0001 = 1
1111 1110 = (invert bits)
1111 1111 = (add 1)
1111 1111 = -1 in twos complement
byte b = 1111 1111
3.1在Java byte
是一个有符号字节,为8位; 而char
是一个无符号的2字节16位。 Java的无法铸造的byte
的char
直接(不能有符号类型转换为无符号类型),因此它投/拓宽byte
到int
第一和缩小到char
。
byte b = -1;
char c = (char) (b); = byte -> int -> char
1111 1111 = byte (8 bits)
1111 1111 | 1111 1111 | 1111 1111 | 1111 1111 = int (32 bits, sign extension)
1111 1111 | 1111 1111 = char (16 bits)
char c = 1111 1111 | 1111 1111
3.2这个(int)c
很有趣; 它将一个char
2字节转换为一个int
4字节。 首先,我们可能认为Sign扩展将执行并用原始的最高有效位(即1)填充增加的位。
char c = 1111 1111 | 1111 1111
int(c) = ???? ???? | ???? ???? | 1111 1111 | 1111 1111
int(c) = 1111 1111 | 1111 1111 | 1111 1111 | 1111 1111 (sign extension) ??? but why the output is 65535?
答案是否定的,在上述情况下,Java使用零扩展将零位填充为增加的位。
字符->任何类型=零扩展
请记住,char
是无符号类型,如果我们将无符号类型char
为其他任何类型,则使用zero extension
,而不是“符号扩展名”。
什么是零扩展?
零扩展将所有增加的位填充为零。 此零扩展名也适用于逻辑右移运算符>>>
。
以下是int(c)
的正确答案。
char c = 1111 1111 | 1111 1111
int(c) = ???? ???? | ???? ???? | 1111 1111 | 1111 1111
int(c) = 0000 0000 | 0000 0000 | 1111 1111 | 1111 1111 (zero extension on unsigned char), the output is 65535.
3.4现在,我们来看以下语句。
c = (char) (b & 0xff); // mask, int -> char
System.out.println((int) c); // char -> int, output: 255, but why?
重写为
int i = (b & 0xff)
c = (char)i
System.out.println((int) c);
b & 0xff
将返回一个int
。 Java的铸byte
到int
,并执行bitwise &
与此0xff
,又名掩盖。
byte b = 1111 1111
int = 1111 1111 | 1111 1111 | 1111 1111 | 1111 1111 (sign extension)
&
0xff = 0000 0000 | 0000 0000 | 0000 0000 | 1111 1111
int i = 0000 0000 | 0000 0000 | 0000 0000 | 1111 1111
c = (char)i = 0000 0000 | 1111 1111 (narrow down, int -> char)
最后一个(int) c
,将一个无符号char
为int
,零扩展名。
c = 0000 0000 | 1111 1111
(int)c = ???? ???? | ???? ???? | 0000 0000 | 1111 1111 (zero extension)
(int)c = 0000 0000 | 0000 0000 | 0000 0000 | 1111 1111
在二进制补码中,第一个最左边定义的符号,1是负数,0是正数。
0000 0000 | 0000 0000 | 0000 0000 | 1111 1111
= 1x2^0 +... 1x2^8
1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 = 255
做完了 感谢您阅读这篇长文章。
注意
如果您发现任何错别字,错误或错误解释,请告诉我。