该技术适用于任何大小的整数,但我将使用 8 位字节大小的整数来解释,因为数字更小并且更容易使用。
An 8-bit type has 28 = 256 possible values. At a low level these are just bits, and signed vs unsigned is a matter of how we interpret those bits. When interpreted as an unsigned integer, they have a range of 0 to 255. When interpreted as a signed two's complement https://en.wikipedia.org/wiki/Signed_number_representations#Two.27s_complement integer, they have a range of −128 to +127.
类型的数轴如下所示:
请注意,从 0 到 127 的正数可以用有符号和无符号类型表示,并且它们由完全相同的位模式(00000000 到 01111111)表示。
在无符号解释中表示从 128 到 255 的大正数的位模式被重新用于有符号解释中的数字 -128 到 -1。就好像有人拿了无符号数轴,切掉了范围的上半部分,然后将其粘在了数轴的下端。
现在,让我们看看比较一对整数时会发生什么。
情况 1:两个值都在有符号正数范围内
由于这两个值都在 0 到 127 范围内,因此无论这些位被解释为有符号还是无符号,它们都具有相同的数值。
我们无条件地将 MIN_VALUE 添加到每个值。我们的有符号字节类型的 MIN_VALUE 是 -128,所以添加这意味着我们实际上是减法 128.
一个例子:我们的比较函数,使用有符号类型,给出x= 20 和y= 60. 添加 MIN_VALUE,我们得到x'= 20 − 128 = −108 且y'= 60 − 128 = −68:
将 MIN_VALUE 添加到正值将始终将其映射为负值。在该范围的最末端,0 将变为 -128,127 将变为 -1。该操作不会改变顺序x and y相对于彼此,因此之间的任何比较的结果x' and y'就像我们没有添加 MIN_VALUE 一样,这是正确的。
情况 2:两个值都在有符号负数范围内
在这种情况下,如果解释为有符号,则两个值都在 -128 到 -1 范围内。如果解释为无符号,它们的范围是 128 到 255(大 256)。
当我们无条件地将 MIN_VALUE 添加到每个带符号的负值时,它always导致溢出和环绕,变成带符号的正值。从数字上看,这种环绕与加 256 相同。如果我们给出x= −35 且y= −80 进行比较,我们得到x'= −35 − 128 + 256 = 93 且y'= −80 − 128 + 256 = 48:
我们还可以用 −35 和 −80 的无符号解释来可视化这一点,即 221 和 176。当减去 128 时,我们得到完全相同的结果x' and y'。二进制补码的优点之一是,无论将数据视为有符号还是无符号,加法和减法都会给出相同的结果,因此 CPU 可以使用相同的电路。
与情况 1 一样,该操作不会更改两个数字之间的任何比较结果。我们的x大于y(负值较小),以及x'也大于y'。因此这些输入之间的比较是正确的。
情况 3:一个值在有符号正数范围内,另一个为负数
这是一个有趣的案例。请注意,当我们添加 MIN_VALUE 时,它总是会更改数字的符号。正值映射到负值,负值映射到正值。
我们来比较一下x= −35 且y= 60。由于我们希望将它们作为无符号进行比较,因此我们确实打算x被解释为−35 + 256 = 221。所以x需要解释为大于y,即使我们的签名数据类型通常不会这样做。
由于数字具有相反的符号,因此更改符号的 MIN_VALUE 运算将反转数字在数轴上的顺序。x'= −35 − 128 + 256 =93, and y'= 60 − 128 =−68。所以我们得到x'大于y',这就是我们想要的:
概括
由于我们已经处理了正负的所有组合,因此我们知道该技术适用于所有可能的值。
对于 32 位整数,范围更大(有符号范围是 −2,147,483,648 (MIN_VALUE) 到 +2,147,483,647,无符号范围是 0 到 4,294,967,295),但工作原理是一样的。事实上,它适用于每种大小的整数以及每种编程语言,前提是:
有符号整数使用二进制补码表示(这几乎是通用的)。
加法在溢出时回绕(而不是引发错误或提升为更大的数字类型或未定义)。
您也可以执行相反的操作:如果您只有无符号整数类型,并且想要进行二进制补码有符号比较,请将有符号最小值(的无符号解释)添加到每个数字。
由于该技术只是两个无条件加法运算,因此即使编译器或虚拟机不进行特殊处理,它也非常高效。