为什么在Java中字节的范围是-128到127?


78

我不明白为什么一个字节可以取的最小值是-128。我可以看到最大值是127,因为它是01111111二进制的,但是一个怎么-128只用8位表示,其中之一用于符号呢?正数128已经是8位,即10000000,那么您将需要第9位来表示负号。

有人可以帮我解释一下。



1
这对其他整数类型相似shortintlong
starblue 2010年

11
一个更好的问题是why java byte type is not a range of 0..255?实际上,许多人的确会问这个问题,在大多数语言中,byte类型都是无符号的,但在Java中byte也是带符号的,而且我(和许多其他人)认为,从一开始就在Java中保留了这种不好的设计。当您使用JNI时会遇到一些问题,当您命名为byte0..255时请相信我!
2014年

Answers:


91

答案是二进制补码

简而言之,Java(和大多数现代语言)不使用有符号大小表示来表示有符号整数。换句话说,8位整数不是符号位,后跟7位无符号整数。

相反,负整数在称为二进制补码的系统中表示,它使硬件更容易进行算术处理,并且还消除了具有正零和负零的潜在歧义。消除负零的副作用是,在范围的底部始终有一个额外的负数可用。

二进制补码系统的另一个有趣特性是,第一位有效地用作符号指示符(即,从位1开头的所有数字均为负),但接下来的七个位不能自己解释为使用符号位。

二进制补码并不是很复杂,但是初步了解二进制补码是什么,以及它如何工作以及为什么起作用,可能超出了SO答案的范围。从Wikipedia文章开始,或在google中搜索更多资源。

为了尝试简短地解决有关-128的查询,生成二进制补码背后的基本思想是采用数字的无符号形式,将所有位取反并加1。因此,无符号128是10000000。取反的是01111111,再加上一个将获得10000000。因此,在二进制补码系统中,10000000明确是-128而不是+128。大于或等于+128的数字根本无法使用二进制补码系统以8位表示,因为它们会与负数形式产生歧义。


整齐!让我们看看我是否正确:以0开头(而不是00000000)的8位数字是正数,而以1开头的8位数是否为负?此外,关于二进制补码的唯一真正棘手的事情是字节的位表示形式与数学类中的值具有不同的值,即10000000通常为+128,但作为字节为-128。亚铁矿?
错误的请求,2010年

没错 当且仅当数字为负时,第一位为1。如果第一位为0,则数字为正或零。
Tyler McHenry'9

@Tyler:我敢打赌,您也可以在此帖子下回答我的问题: stackoverflow.com/questions/16775169 / ... 我希望这不是太直接,但我真的很奇怪,经过几个月的冗长搜索,我仍然没有线索:〜
JBA

该代码字节b1 = 0b10000000; 使编译错误?
zeds 2015年

30

补码的工作原理如下:

一个字节由8位组成。

00000000表示0

11111111表示255

但是,如果以这样的方式显示数字,则我们将不会区分结果数字是正数还是负数。由于这个原因,左侧的位为我们提供了此信息。如果左侧的位为0,则可以开始在的顶部添加其他位的值zero。如果是位1,则应在的顶部开始添加-128。因为左侧的位是2到7的幂。

例子;

在这些示例中,左侧的位为1,这意味着我们要在-128的顶部添加其他位的值。

10000000 = -128(-128 + 0)

10000001 = -127(-128 +1)

10000011 = -125(-128 + 3)

10000111 = -121(-128 + 7)

相同的位,但是这次,左边的位是0。这意味着我们开始在的顶部添加0

00000000 = 0(0 + 0)

00000001 = 1(0 +1)

00000011 = 3(0 + 3)

00000111 = 7(0 + 7)

如果到目前为止还可以,请回答您的问题,

最小的数字

10000000 = -128

可能的最大数量

011111111 = 127

这就是为什么范围在-128到127之间的原因。


8

正如詹姆斯在其评论中指出的那样,这是因为这就是补码的工作原理。

如果换句话说,您可以表示2 ^ 8 = 256种值。在这种情况下,它用作128个负数,127个正数和零。如果我们使用7位来表示值,而使用+1位来表示符号,则我们可以表示少一个值,并且还可以具有两个零(这很不幸,因为比较两个值将因此更加复杂)。


3

基本数字类型可以表示2 ^ n个数字。看n = 2的情况。您可以代表四种情况,我们称它们为a,b,c,d。然后,您可以同意a=-2, b=-1, c=0, d=1(这是公认的方式)或 a=-1, b=0, c=1, d=2(可能,但未使用)。因此,如果您只有一个0并保持2 ^ n的状态,表示您abs(min) != max增加n边界移动量,但abs(min) != max仍然保持。



1

在Java中,所有变量(如byte short int long float float double)均按有符号形式编写。因此,非常简单的是,首位始终指定什么(负数或正数),但是由于数字可除以2的一半被移位为负数,因此默认情况下0为正数。所以看起来像这样:

这是正
+ | 0001001
1 | 0001001
这是负
-| 0001001
0 | 0001001,
因为一个字节短,负为
-000000011111111
0000000011111111


0

无需取二进制补码:2 ^ 8(因为一个字节是8位数字,并且可以具有2个值中的1个)= 256,所以一个字节可以代表的最单独的值是256。因此,表示数字-128到-1是我们范围的一半。我相信这里的问题是为什么最大正值是127,而不是128。这是因为我们必须表示数字0,所以0-127是我们范围内的其他128个可能性。

如果我们只允许使用正值,例如不可能使用负数的无符号字节,则范围将是0-255,因为它们是256个不同的值(包括0)。


0

在取完两个的数字后,我们总是留下一个表示数字多余的状态,因此我们将该状态变为-128。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.