如何理解java的有符号数

首先我们需要区分的是什么是有符号数和无符号数。
有符号和无符号的区别是一个有符号位,另一个没有符号位. 
没有符号位的数字只能有0和正值,有符号位的数字可以有正零,负零和正数负数.
从这里可以看出,有符号和无符号的区别就是是否能表示负数。

然后我们再来理解java虚拟机所支持的所有整数数据类型-byte,short,int和long,他们都是带符号的二进制补码。
那么为什么会采用二进制补码呢?
首先我们还是来看二进制补码的概念:
负数的补码就是对反码加1,而正数不变,正数的原码反码补码是一样的.
若要用补码系统表示 -5,首先要将 5 的二进制进行反相运算,再加 1。
0000 0101 (5) -----1111 1010-----1111 1011 (-5) 补码
然后我们再看一个例子:
       ( 1 ) - ( 1 )
( 1 ) + ( -1 )
(00000001)+ (10000001) -----------------原码计算
= (10000010)= ( -2 )
显然原码对于负数的运算无效。
所以,补码主要是针对2进制的负数运算产生的。

它的原理是什么呢,我们可以看一个在《Code》里面举的例子:

253 – 176 = ???

可以看到,这个减法运算在个位的时候,就需要向前借位了,十分麻烦。

但是我们可以转变一下思路,首先这是一个三位数,三位数的最大值是999。因此我们先用999减去减数176

999 – 176 = 823//这一步是得到他的反码

然后,用被减数253加上上面求出来的这个值823加1

253 + (823 +1)= 1077

然后把这个值再减去1000,这样就得到了77。

253+(999-176)+1-1000=77

然后我们再来看二进制补码计算:

 

   0011  (3)  //正数的补码是它本身
  + 1111 (-1)   //负数的补码是反码加1 即0001》1110+1》1111
--------------
   10010  (2)

 

结果 10010 似乎是错的,因为已经超过四个比特,不过若忽略掉(从右数起的)第 5 个比特,结果是 0010 (2),和我们计算的结果一样。而且若可以将二进制的 0001 (1) 变号为 1111 (-1),以上的式子也可以计算减法:3-1 = 2。

 

 

补码的工作原理

学习计算机科学的学生会问:为什么补码能这么巧妙实现了正负数的加减运算?答案是:指定n比特字长,那么就只有2n个可能的值,加减法运算都存在上溢出与下溢出的情况,实际上都等价于2n的加减法运算。这对于n比特无符号整数类型或是n比特有符号整数类型都同样适用。

例如,8位无符号整数的值的范围是0到255. 因此4+254将上溢出,结果是2,即(4+254) \equiv 258 \equiv 2 \pmod{256}

例如,8位有符号整数的值的范围,如果规定为−128到127, 则126+125将上溢出,结果是−5,即(126+125) \equiv 251 \equiv -5 \pmod{256}

对于8位字长的有符号整数类型,以28即256为模,则


\begin{align}
-128 & \equiv 128 \pmod{256} \\
-127 & \equiv 129 \pmod{256} \\
\vdots \\
-2 & \equiv 254 \pmod{256} \\
-1 & \equiv 255 \pmod{256} \\
\end{align}

所以模256下的加减法,用0, 1, 2, …, 254,255表示其值,或者用−128, −127,… , −1, 0, 1, 2,… ,127是完全等价的。−128与128,−127与129,…,−2与254,−1与255可以互换而加减法的结果不变。从而,把8位(octet)的高半部分(即二进制的1000 0000到1111 1111)解释为−128到−1,同样也实现了模256的加减法,而且所需要的CPU加法运算器的电路实现与8位无符号整数并无不同。

实际上对于8比特的存储单元,把它的取值[00000000, …, 11111111]解释为[0, 255], 或者[-1, 254], 或者[-2, 253], 或者[-128, 127], 或者[-200, 55], 甚至或者[500, 755], 对于加法硬件实现并无不同。

 

Since bytes have such a small range, they're often converted to ints in calculations and method invocations. Often, they need to be converted back, generally through a cast. Therefore, it's useful to have a good grasp of exactly how the conversion occurs. 


Casting from an int to a byte for that matter, casting from any wider integer type to a narrower typetakes place through truncation of the high-order bytes. This means that as long as the value of the wider type can be expressed in the narrower type, the value is not changed. The int 127 cast to a byte still retains the value 127. 

On the other hand, if the int value is too large for a byte, strange things happen. The int 128 cast to a byte is not 127, the nearest byte value. Instead, it is -128. This occurs through the wonders of two's complement arithmetic. Written in hexadecimal, 128 is 0x00000080. When that int is cast to a byte, the leading zeros are truncated, leaving 0x80. In binary, this can be written as 10000000. If this were an unsigned number, 10000000 would be 128 and all would be fine, but this isn't an unsigned number. Instead, the leading bit is a sign bit, and that 1 does not indicate 27 but a minus sign. The absolute value of a negative number is found by taking the complement (changing all the 1 bits to 0 bits and vice versa) and adding 1. The complement of 10000000 is 01111111. Adding 1, you have 01111111 + 1 = 10000000 = 128 (decimal). Therefore, the byte 0x80 actually represents -128. Similar calculations show that the int 129 is cast to the byte -127, the int 130 is cast to the byte -126, the int 131 is cast to the byte -125, and so on. This continues through the int 255, which is cast to the byte -1. 

posted @ 2013-02-25 21:51  sqtds  阅读(1590)  评论(0编辑  收藏  举报