5

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.

Kevin Cruijssen
  • 9,153
  • 9
  • 61
  • 135
  • What's the purpose of this? – Kayaman Sep 26 '17 at 08:20
  • 1
    @Kayaman Curiosity. :) To be honest, it doesn't really have a purpose. I'm just curious how these calculations work and how to apply it yourself with other min/max. A semi-related purpose is to somehow use it to create a code-golf challenge (which is off-topic for this question). It's mainly curiosity and a way to reproduce this behavior for other min/max values. – Kevin Cruijssen Sep 26 '17 at 08:24
  • Extending the Number class and create your own data type and then implement add/subtract/multiple/divide operations might achieve this. – Chota Bheem Sep 26 '17 at 08:25

2 Answers2

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.

harold
  • 61,398
  • 6
  • 86
  • 164
  • The method you've displayed works perfectly for integers. But I'm still a bit confused about the rest. If I recall correctly, actual modulo instead of remainder can be done with `(val % mod + mod) % mod` in Java (or `Math.floorMod(val, mod)` since Java 8). So if `min=-10; max=10` I would start with `n = Math.floorMod(n, 21);`(with 21 being `max * 2 + 1`)? But then? If I try `n %= 21` and `return n > max ? n - (max * 2 + 1) : n;` (to map `20` to `-1`, `19` to `-2`, etc.) it gives incorrect results. And more corresponding to the integers, it could also be `min=-10; max=9` instead of `max=10`. – Kevin Cruijssen Sep 26 '17 at 09:41
  • [Here is an idione.com link with `min=-10; max=10`](https://ideone.com/araWBy) (because TIO is too long and I can't use url-shorteners in comments on SO..). [And here is the same code with `min=-10; max=9` instead](https://ideone.com/VIR6G9). (First outputs are expected; second output are from the `reproduceMinMaxFromLongToInt` method; the third boolean output is `true` if they are equal.) – Kevin Cruijssen Sep 26 '17 at 09:51
  • @KevinCruijssen I think you miscounted though, eg 10 * 2 is really -1 (for example by adding 10: add the first 1 to get -10, then there are 9 left and clearly -10 + 9 = -1) – harold Sep 26 '17 at 09:55
  • Ah, you're right that I did the `min=-10; max=10` incorrect regarding the expected results.. My bad. But with `min=-10; max=9` it can be compared to the `Integer.MIN_VALUE`/`Integer.MAX_VALUE`, and since `Integer.MAX_VALUE * 2 = -2` I assume the expected result for `9 * 2` (with `max=9`) should also result in -2. – Kevin Cruijssen Sep 26 '17 at 10:09
  • 1
    @KevinCruijssen well the problem there is that the size of the range is not `max * 2 + 1` anymore, that only works if it's symmetric, in general it's `max - min + 1` – harold Sep 26 '17 at 10:13
  • Ah, perfect. Changing `max * 2 + 1` to `max - min + 1` will indeed hold correct results for all `min=-10; max=9` test cases. And regarding `min=-10; max=10` the expected results I used are simply incorrect. Thanks a lot for the help! – Kevin Cruijssen Sep 26 '17 at 10:43
0

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.

Horia Coman
  • 8,681
  • 2
  • 23
  • 25
  • Yes, I know I can indeed cast them to int to have the correct results. But the question is most about using any kind of min/max. I just used integer as example. I'll clarify this in the question, but what should the method become if I use `min=-100`, `max=99` for example? – Kevin Cruijssen Sep 26 '17 at 08:16