为什么有符号字节的取值范围是从-128到127(二进制补码),而不是从-127到127?

31

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

128的二进制表示为10000000。反转后得到01111111,再加一变成10000000

因此结论是-128的二进制表示为10000000。

+128在8位二进制补码下无法表示,但这意味着我们可以用9位二进制来表示它,所以128的二进制表示为010000000,其2's补码为-128即110000000。

-128的表示是10000000还是110000000?表示是否与比特位有关?

为什么不直接将8位下限设为-127,而要将-128写为10000000?

5个回答

51

为什么无符号字节的范围是从-128到127?

事实上并不是这样。一个无符号字节(假设为8位)的范围是从0到255。

使用二进制补码的有符号字节的范围是从-128到127,这直接来源于二进制补码的定义:

01111111 = +127
01111110 = +126
01111101 = +125
...
00000001 = +1
00000000 =  0
11111111 = -1
...
10000010 = -126
10000001 = -127
10000000 = -128

那么 -128 的表示是 10000000 还是 110000000?

在8位中,它是10000000,在一个假设的9位表示中,它是110000000

为什么不直接将8位的最小值设为-127呢?

将范围人为限制到-127并没有实现多少好处;你会排除一个完全有效的值,并且通常使代码更加复杂(否则你会用10000000这个比特模式做什么?)。


6
当我开始回答时,你的回答只有一句话。现在我看到你的回答已经基本使我的回答过时了。你总是这样做吗?我的意思是,先回答一个简短的话以抢占第一位,然后逐渐改进它吗?从你的声誉值来看,我猜你很擅长这种技巧。+1 - Aftnix
1
@Aftnix:最初它只是一行代码,因为我认为那就是问题所需的。然后有人给它投了反对票,所以我想我应该改进它! - Oliver Charlesworth
@Aftnix:没关系,我不会生气的! - Oliver Charlesworth

19
-128的二进制表示是10000000或11000000吗?2的补码表示法是否与位数相关?是的,2的补码表示法与位数相关。为什么不把8位的最小值设为-127,而是写成10000000表示-128呢?因为2的8次方等于256,所以无论使用哪种表示方案,都应该能够表示256个不同的值。你可以画一个圆来理解2的补码系统有多好。首先看一下这张表:
Bits    Unsigned 2's complement
00000000    0   0
00000001    1   1
00000010    2   2
01111110    126     126
01111111    127     127
10000000    128     −128
10000001    129     −127
10000010    130     −126
11111110    254     −2
11111111    255     −1

对于二进制补码系统,你可以画一个圆来理解这个系统。

这是4位版本。你可以轻松地自己开发一个8位版本。这个圆代表了这个二进制补码系统的实际意义。它是一个循环系统。这意味着它的表示取决于你给它的"跨度",因此同样的负数的8位版本将与16位版本不同。你可以将圆中给出的4位版本与表格中给出的8位版本中的相同负数进行比较。

                      0000  0
                 1111  -1     0001  1


        1110  -2                       0010  2




  1101  -3                                   0011  3



1100  -4                                       0100  4



  1011  -5                                   0101  5




        1010  -6                       0110  6


                 1001  -7     0111  7
                          1000  -8

顺便说一下,二进制补码算术与计算机内部的“固定”宽度计算存储(寄存器、内存等)相容。

在第一代计算机中,存在提供本机十进制算术的倾向。但很快就被废弃,转而采用“补码”或“循环”方案,因为从计算机的角度来看,十进制算术是奇怪的。我们之所以认为它很自然,是因为我们“有10个手指”。这些手指是我们祖先最早的计算工具。这就是为什么我们发现十进制系统是如此自然,它已经融入了我们的基因。


1
+1 更数学化地说,2的补码有符号和无符号只是使用不同的代表来进行模256算术运算。这意味着加法和乘法的结果都会产生相同的位模式,与有符号或无符号表示无关。 - starblue
这意味着它的表示取决于您给它的“跨度”。但是模式非常简单。将符号扩展到更宽的类型只意味着将高位复制到所有新位。 - Peter Cordes
同@starblue所说,使用二进制补码的优点在于:从Wikipedia上可以看到:“一个数和它的二进制补码之和总是等于0(因为最后一位被截断了),而一个数和它的反码之和总是等于-0。二进制补码系统的优点在于,加法、减法和乘法这些基本算术运算与无符号二进制数完全相同”。 - ruffin

6

两个补码的替代方案包括:

  • 反码,由于其“负零”的存在而存在问题
  • 符号/大小,也有一个负零
  • 不为10000000分配含义,在这种情况下,许多接受有符号8位整数的函数都必须检查该无效值,浪费时间。(除非您的代码运行在将此位模式视为整数NaN的假设硬件上。)

最好是给那个位模式分配一个含义,在两个补码表示中自然的含义是-128。

例如,在两个补码中,检查是否为负数相当于检查其最高位是否被设置。在一个10000000无效的变体中,它是(伪代码)

if (highest_bit_zero(x))
    return false;
else if (x == 0b10000000)
    ERROR
else
    return true;

你可以决定如何处理错误 :)

另一个主要的选择是符号/大小,它也有负零。ISO C和C++标准都指定有符号整数类型可以是二进制补码、反码或符号/大小。 - Peter Cordes
如果你只是禁止使用 -128,你可以将其视为 NaN,就像浮点数一样。如果你使用与 IEEE FP 相同的语义,比较可能有 4 种结果:大于、等于、小于或无序。因此,如果 x0x80,则 x < 0 将为 false(无序)。但是 x > 0 也将为 false,x == 0 也将为 false。x == x 也是如此。 - Peter Cordes

3
在一个9位的世界里,它将是110000000。在16位的世界中,它将是1111111110000000。至少,在我们讨论二进制补码的情况下是这样。

正如larsmans指出的那样,如果将范围改为-127,你最终会得到一个“无效”值,而你需要不断对其进行检查。二进制补码被选择是因为ALU很容易处理。就像字节宽度被选择为2的幂一样(这也不总是这种情况)。在硬件级别上,二进制补码加法与无符号加法是相同的,因此不需要特殊指令或额外的硬件(与一的补码相比)。

按照现在的方式,所有最高位设置为1的值都是负数,所有最高位未设置的值都是非负数(正数或零)。很容易,是吗?负数范围比正数范围大1只是为了保持二进制补码简单。

2
无法使用8位二进制表示从enter image description hereenter image description here的范围,因为enter image description hereenter image description here具有相同的模式。非零整数及其负数不能由相同的模式表示。因此,enter image description here无法用8位二进制表示。在8位二进制中,最大的正整数是enter image description here
1000 0000代表什么数字?将enter image description here的表示方法加到它上面: enter image description here ?的一个好选择是enter image description here。因此,1000 0000代表enter image description here。8位二进制可以用来表示数字enter image description here...0...enter image description here

在这里输入图片描述

例如,使用二进制补码表示的八位整数范围为:

Example

请注意,比正整数多一个负整数可以被表示。

来源:- http://programmedlessons.org/AssemblyTutorial/Chapter-08/ass08_20.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接