添加MIN_VALUE如何将整数比较为无符号?

时间:2014-12-17 14:34:44

标签: language-agnostic integer comparison unsigned signed

在Java中,int类型已签名,但它有a method,它将两个整数比较为无符号:

public static int compareUnsigned(int x, int y) {
    return compare(x + MIN_VALUE, y + MIN_VALUE);
}

它为每个参数添加Integer.MIN_VALUE,然后调用普通的签名比较方法,即:

public static int compare(int x, int y) {
    return (x < y) ? -1 : ((x == y) ? 0 : 1);
}

如何在每个参数中添加MIN_VALUE神奇地使比较无符号?

1 个答案:

答案 0 :(得分:10)

此技术适用于任何大小的整数,但我会使用8位字节大小的整数来解释,因为数字更小且更易于使用。

8位类型具有2个 8 = 256个可能的值。在低级别,这些只是位,而签名与无符号是我们如何解释这些位的问题。当解释为无符号整数时,它们的范围为0到255.当解释为带符号的two's complement整数时,它们的范围为-128到+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&#39; = 20 - 128 = -108且 y&#39; = 60 - 128 = -68:

将MIN_VALUE添加到正值将始终将其映射为负值。在范围的最末端,0将变为-128,127将变为-1。该操作不会改变 x y 相对于彼此的顺序,因此 x&#39; 和<之间的任何比较结果em> y&#39; 与我们没有添加MIN_VALUE相同,这是正确的。

案例2:两个值都在有符号的负范围

在这种情况下,如果解释为signed,则两个值都在-128到-1的范围内。如果解释为无符号,则它们在128到255范围内(大于256)。

当我们无条件地将MIN_VALUE添加到每个有符号的负值时,始终会导致溢出和环绕,转换为带符号的正值。在数字上,这个环绕与添加256相同。如果我们给 x = -35和 y = -80进行比较,我们得到 x&# 39; = -35 - 128 + 256 = 93且 y&#39; = -80 - 128 + 256 = 48:

我们也可以使用-35和-80的无符号解释来显示它,它们分别为221和176.当减去128时,我们得到 x&#39; ý&#39; 。两个补码的优点之一是无论您是将数据视为有符号还是无符号,加法和减法都会产生相同的结果,因此CPU可以使用相同的电路。

与情况1一样,该操作不会改变两个数字之间任何比较的结果。我们的 x 大于 y (负面幅度较小), x&#39; 也大于 y&#39 ; 。因此,这些输入之间的比较是正确的。

案例3:一个值在有符号的正范围内,另一个为负

这是一个有趣的案例。请注意,当我们添加MIN_VALUE时,它总是会更改数字的符号。正值映射到负值,负值映射到正值。

让我们比较 x = -35和 y = 60.由于我们希望将它们作为无符号进行比较,我们真的打算 x < / em>被解释为-35 + 256 = 221.所以 x 需要被解释为大于 y ,即使我们的签名数据类型通常不会这样做

因为数字有相反的符号,改变符号的MIN_VALUE操作将反转数字&#39;在数字线上订购。 x&#39; = -35 - 128 + 256 = 93 y&#39; = 60 - 128 = -68 即可。所以我们得到 x&#39; 大于 y&#39; ,这就是我们想要的:

概括

由于我们处理了所有正面和负面的组合,我们知道该技术适用于所有可能的值。

在32位整数的情况下,范围更大(有符号范围是-2,147,483,648(MIN_VALUE)到+2,147,483,647,无符号范围是0到4,294,967,295)但它的工作方式相同。事实上,它适用于每个大小的整数,并且在每种编程语言中,只要:

  1. 签名整数使用两个补码表示(几乎是通用的)。

  2. 在溢出时添加包围(而不是引发错误或提升为更大的数字类型或未定义)。

  3. 您也可以反过来:如果您只有一个无符号整数类型,并且想要进行二进制补码签名比较,请将已签名最小值(无符号解释)添加到每个数字。< / p>

    因为该技术只是两个无条件的加法操作,即使没有被编译器或VM专门处理,它也非常有效。