Reproduce behavior MAX_VALUE and MIN_VALUE

2019-04-10 02:51发布

问题:

The following also applied to other MIN_VALUE and MAX_VALUE, but let's only focus on Integer for now. I know that in Java integers are 32-bit, with Integer.MAX_VALUE = 2147483647 (231-1) and Integer.MIN_VALUE = -2147483648 (-231). When calculating with these values when you go beyond their bounds, the number wraps around / overflows. So when you do something like Integer.MAX_VALUE + 1, the result is the same as Integer.MIN_VALUE.

Here are some basic arithmetic calculations with MIN_VALUE and MAX_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

Or more in general (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

My question is: how can I reproduce all basic arithmetic operations (+-*/) above manually?

The first thing that came to mind was the modulo operator. So I tried a simple method like this:

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

Which is correct for most of them, but not all. (To reduce the question size, here is a TIO link with test code, instead of a copy-paste here.) The ones that are incorrect:

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

The others are correct.

How can I modify the reproduceMinMaxFromLongToInt method so it gives the correct result for all basic arithmetic calculations (ignoring calculations like Power, Modulo, Root, and such for now)?
I know I should probably look at bit-wise operands for the most part, but is it possible to reproduce this behavior without bit-wise operands, using basic arithmetic operands (including modulo) only?

EDIT: Note: The Integer is just used as an example. Of course I could just cast to int in this case. But I'm trying to figure out the more general algorithm which also applied to other min/max, like min=-100; max=99 for example.

回答1:

Here's one without bitwise operations (I'm not counting the constant-generation, they could be written out but it would obscure their meaning) or casts, as you can see it's more complicated than it should be, and it would be worse without 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);
}

Implementing other pairs of min/max this way is probably a strange thing to do. A more reasonable approach, probably, is to work only with positive numbers (in Java) modulo the length of the range, and interpret the upper range of them as being negative.

For example if the range was -2 through 2, you could bring them all into 0..4 by mapping them modulo (actual modulo, not Java-style remainder) 5. Then the usual mod-5 arithmetic will act reasonably. In the end just map them back to the original range, by interpreting 4 as -1 (which is, in mod-5 arithmetic, a reasonable thing to say) and 3 as -2.

You could interpret the code above as doing that, but there is the weird issue that (due to the range involved) it had to work with signed numbers as if they were unsigned, so Long.remainderUnsigned made an appearance. For small ranges that wouldn't be a problem.



回答2:

A solution, though perhaps not the one you want is to convert the numbers to int, and then do the arithmetic operation on ints. You'd finally convert back to long to keep the contract of your method.