番石榴的UnsignedLong:为什么会XOR Long.MIN_VALUE

时间:2018-11-22 22:12:54

标签: java guava long-integer

我正在阅读Unsigned arithmetic in Java,它很好地解释了如何使用以下方法进行无符号的加长操作

public static boolean isLessThanUnsigned(long n1, long n2) {
  return (n1 < n2) ^ ((n1 < 0) != (n2 < 0));
}

但是我对Guava的实现感到困惑。我希望有人能对此有所启发。

  /**
   * A (self-inverse) bijection which converts the ordering on unsigned longs to the ordering on
   * longs, that is, {@code a <= b} as unsigned longs if and only if {@code flip(a) <= flip(b)} as
   * signed longs.
   */
  private static long flip(long a) {
    return a ^ Long.MIN_VALUE;
  }

  /**
   * Compares the two specified {@code long} values, treating them as unsigned values between
   * {@code 0} and {@code 2^64 - 1} inclusive.
   *
   * @param a the first unsigned {@code long} to compare
   * @param b the second unsigned {@code long} to compare
   * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is
   *     greater than {@code b}; or zero if they are equal
   */
  public static int compare(long a, long b) {
    return Longs.compare(flip(a), flip(b));
  }

2 个答案:

答案 0 :(得分:1)

也许某些图表有帮助。我将使用8位数字来使常量保持简短,以明显的方式将其推广为int和long。

绝对视图:

Unsigned number line:
                [ 0 .. 0x7F ][ 0x80 .. 0xFF]
Signed number line:
[ 0x80 .. 0xFF ][ 0 .. 0x7F]

相对视图:

Unsigned number line:
[ 0 .. 0x7F ][ 0x80 .. 0xFF]
Signed number line:
[ 0x80 .. 0xFF ][ 0 .. 0x7F]

因此,有符号和无符号数字在很大程度上具有相同的相对顺序,不同的是按顺序交换了设置了符号位和未设置符号位的两个范围。当然,反转该位会交换顺序。

x ^ Long.MIN_VALUE反转long的符号位。

此技巧适用于仅依赖于相对顺序的任何操作,例如比较和直接相关的操作(例如最小和最大)。它不适用于依赖于数字的绝对大小的运算,例如除法。

答案 1 :(得分:0)

请考虑组成long类型的位。执行^ Long.MIN_VALUE会将包含[-2 63 ,2 63-1 ]值的常规two's complement有符号表示转换为包含[ 0,2 64-1 ]值。

您可以通过取最小的long值,然后在检查这些位(例如使用Long.toBinaryString())时加“ 1”并“翻转”来查看该过程:

  • Long.MIN_VALUE ^ Long.MIN_VALUE00..00 (未设置所有64位)
  • (Long.MIN_VALUE + 1) ^ Long.MIN_VALUE00..01
  • (Long.MIN_VALUE + 2) ^ Long.MIN_VALUE00..10
  • (Long.MIN_VALUE + 3) ^ Long.MIN_VALUE00..11

依次类推,直到:

  • Long.MAX_VALUE ^ Long.MIN_VALUE11..11 (已设置所有64位)

完成“翻转”是因为Longs.compare()需要按照示例中的javadoc方法输入无符号[0,2 64-1 ]值:

/**
 * Compares the two specified {@code long} values, treating them as unsigned values between
 * {@code 0} and {@code 2^64 - 1} inclusive.
 *