重现行为 MAX_VALUE 和 MIN_VALUE

Reproduce behavior MAX_VALUE and MIN_VALUE

以下内容也适用于其他 MIN_VALUEMAX_VALUE,但我们现在只关注 Integer。我知道 Java 整数是 32 位的,Integer.MAX_VALUE = 2147483647 (231-1) 和 Integer.MIN_VALUE = -2147483648 (-231)。当您超出范围时使用这些值进行计算时,数字会环绕/溢出。因此,当您执行类似 Integer.MAX_VALUE + 1 的操作时,结果与 Integer.MIN_VALUE.

相同

下面是MIN_VALUEMAX_VALUE的一些基本算术计算:

Integer.MAX_VALUE:                      2147483647
Integer.MAX_VALUE + 1:                  -2147483648
Integer.MAX_VALUE - 1:                  2147483646
Integer.MAX_VALUE * 2:                  -2
Integer.MAX_VALUE * 3:                  2147483645
Integer.MAX_VALUE * 4:                  -4
Integer.MAX_VALUE * 5:                  2147483643
Integer.MAX_VALUE / Integer.MAX_VALUE:  1
Integer.MAX_VALUE * Integer.MAX_VALUE:  1
Integer.MAX_VALUE / Integer.MIN_VALUE:  0
Integer.MAX_VALUE * Integer.MIN_VALUE:  -2147483648
Integer.MAX_VALUE - Integer.MIN_VALUE:  -1
Integer.MAX_VALUE + Integer.MIN_VALUE:  -1
-Integer.MAX_VALUE:                     -2147483647
-Integer.MAX_VALUE - 1:                 -2147483648
-Integer.MAX_VALUE + 1:                 -2147483646
Integer.MIN_VALUE:                      -2147483648
Integer.MIN_VALUE + 1:                  -2147483647
Integer.MIN_VALUE - 1:                  2147483647
Integer.MIN_VALUE * 2:                  0
Integer.MIN_VALUE * 3:                  -2147483648
Integer.MIN_VALUE * 4:                  0
Integer.MIN_VALUE * 5:                  -2147483648
Integer.MIN_VALUE / Integer.MAX_VALUE:  -1
Integer.MIN_VALUE / Integer.MIN_VALUE:  1
Integer.MIN_VALUE * Integer.MIN_VALUE:  0
Integer.MIN_VALUE - Integer.MAX_VALUE:  1
-Integer.MIN_VALUE:                     -2147483648
-Integer.MIN_VALUE - 1:                 2147483647
-Integer.MIN_VALUE + 1:                 -2147483647

或更普遍(iff MIN == -MAX-1):

MAX:                      MAX
MAX + 1:                  MIN
MAX - 1:                  MAX - 1
MAX * 2:                  -2
MAX * 3:                  MAX - 2
MAX * 4:                  -4
MAX * 5:                  MAX - 4
MAX / MAX:                1
MAX * MAX:                1
MAX / MIN:                0
MAX * MIN:                MIN
MAX - MIN:                -1
MAX + MIN:                -1
-MAX:                     MIN + 1
-MAX - 1:                 MIN
-MAX + 1                  MIN + 2
MIN:                      MIN
MIN + 1:                  MIN + 1
MIN - 1:                  MAX
MIN * 2:                  0
MIN * 3:                  MIN
MIN * 4:                  0
MIN * 5:                  MIN
MIN / MAX:                -1
MIN / MIN:                1
MIN * MIN:                0
MIN - MAX:                1
-MIN:                     MIN
-MIN - 1:                 MAX
-MIN + 1:                 MIN + 1

我的问题是:如何手动重现上述所有基本算术运算 (+-*/)?

首先想到的是模运算符。所以我尝试了这样一个简单的方法:

long reproduceMinMaxFromLongToInt(long n){
  if(n > 2147483647L){
    return n % 2147483648L;
  }
  if(n < -2147483648L){
    return n % -2147483648L;
  }
  return n;
}

这对大多数人来说是正确的,但不是全部。 (为了减少问题的大小,here is a TIO link 使用测试代码,而不是在此处复制粘贴。)不正确的:

Calculation:                Should be       But is instead

MAX_VALUE + 1:              -2147483648     0
MAX_VALUE * 2:              -2              2147483646
MAX_VALUE * 4:              -4              2147483644
MAX_VALUE * MIN_VALUE:      -2147483648     0
MAX_VALUE - MIN_VALUE:      -1              2147483647
MIN_VALUE - 1:              2147483647      -1
MIN_VALUE * 3:              -2147483648     0
MIN_VALUE * 5:              -2147483648     0
-MIN_VALUE - 1:             2147483647      2147483647

其他都是正确的。

如何修改 reproduceMinMaxFromLongToInt 方法,使其为所有基本算术计算提供正确的结果(暂时忽略幂、模、根等计算)?
我知道我可能应该在很大程度上查看按位操作数,但是是否可以在不使用按位操作数的情况下仅使用基本算术操作数(包括模数)来重现此行为?

编辑:注意:Integer 仅用作示例。当然,在这种情况下我可以转换为 int 。但我正在尝试找出更通用的算法,该算法也适用于其他 min/max,例如 min=-100; max=99

一种解决方案(虽然可能不是您想要的)是将数字转换为 int,然后对整数进行算术运算。您最终会转换回 long 以保持您方法的约定。

这是一个没有按位运算的(我没有计算常量生成,它们可以写出来,但它会模糊它们的含义)或强制转换,正如你所看到的,它比它应该的更复杂,而且它没有 Java 会更糟 8:

long reproduceMinMaxFromLongToInt(long n){
    // reduce range
    n = Long.remainderUnsigned(n, 1L << 32);
    // sign-extend
    if (n < (1L << 31))
        return n;
    else
        return n - (1L << 32);
}

以这种方式实现其他对 min/max 可能是一件奇怪的事情。一种更合理的方法可能是仅使用正数(在 Java 中)modulo 范围的长度,并将它们的上限解释为负数。

例如,如果范围是 -2 到 2,您可以通过将它们映射 modulo(实际 modulo,而不是 Java 将它们全部变成 0..4 -style remainder) 5. 那么通常的 mod-5 算术将合理运行。最后,通过将 4 解释为 -1(在 mod-5 算术中,这是一个合理的说法)并将 3 解释为 -2.

,将它们映射回原始范围

您可以将上面的代码解释为这样做,但是有一个奇怪的问题(由于涉及的范围)它必须处理有符号数字,就好像它们是无符号的一样,所以 Long.remainderUnsigned 做了一个外貌。对于小范围,这不是问题。