105

This code:

System.out.println(Math.abs(Integer.MIN_VALUE));

Returns -2147483648

Should it not return the absolute value as 2147483648 ?

gideon
  • 19,329
  • 11
  • 72
  • 113
user665319
  • 1,419
  • 3
  • 14
  • 13

8 Answers8

119

Integer.MIN_VALUE is -2147483648, but the highest value a 32 bit integer can contain is +2147483647. Attempting to represent +2147483648 in a 32 bit int will effectively "roll over" to -2147483648. This is because, when using signed integers, the two's complement binary representations of +2147483648 and -2147483648 are identical. This is not a problem, however, as +2147483648 is considered out of range.

For a little more reading on this matter, you might want to check out the Wikipedia article on Two's complement.

NullUserException
  • 83,810
  • 28
  • 209
  • 234
jonmorgan
  • 2,570
  • 2
  • 22
  • 27
  • 9
    Well, not a problem is underestimating the impact, it could very well mean problems. Personally I would rather have an exception or a number system that grows dynamically in a higher level language. – Maarten Bodewes Dec 19 '12 at 23:47
49

The behaviour you point out is indeed, counter-intuitive. However, this behaviour is the one specified by the javadoc for Math.abs(int):

If the argument is not negative, the argument is returned. If the argument is negative, the negation of the argument is returned.

That is, Math.abs(int) should behave like the following Java code:

public static int abs(int x){
    if (x >= 0) {
        return x;
    }
    return -x;
}

That is, in the negative case, -x.

According to the JLS section 15.15.4, the -x is equal to (~x)+1, where ~ is the bitwise complement operator.

To check whether this sounds right, let's take -1 as example.

The integer value -1 is can be noted as 0xFFFFFFFF in hexadecimal in Java (check this out with a println or any other method). Taking -(-1) thus gives:

-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1

So, it works.

Let us try now with Integer.MIN_VALUE . Knowing that the lowest integer can be represented by 0x80000000, that is, the first bit set to 1 and the 31 remaining bits set to 0, we have:

-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1 
                     = 0x80000000 = Integer.MIN_VALUE

And this is why Math.abs(Integer.MIN_VALUE) returns Integer.MIN_VALUE. Also note that 0x7FFFFFFF is Integer.MAX_VALUE.

That said, how can we avoid problems due to this counter-intuitive return value in the future?

  • We could, as pointed out by @Bombe, cast our ints to long before. We, however, must either

    • cast them back into ints, which does not work because Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE).
    • Or continue with longs somehow hoping that we'll never call Math.abs(long) with a value equal to Long.MIN_VALUE, since we also have Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE.
  • We can use BigIntegers everywhere, because BigInteger.abs() does indeed always return a positive value. This is a good alternative, though a bit slower than manipulating raw integer types.

  • We can write our own wrapper for Math.abs(int), like this:

/**
 * Fail-fast wrapper for {@link Math#abs(int)}
 * @param x
 * @return the absolute value of x
 * @throws ArithmeticException when a negative value would have been returned by {@link Math#abs(int)}
 */
public static int abs(int x) throws ArithmeticException {
    if (x == Integer.MIN_VALUE) {
        // fail instead of returning Integer.MAX_VALUE
        // to prevent the occurrence of incorrect results in later computations
        throw new ArithmeticException("Math.abs(Integer.MIN_VALUE)");
    }
    return Math.abs(x);
}
  • Use a integer bitwise AND to clear the high bit, ensuring that the result is non-negative: int positive = value & Integer.MAX_VALUE (essentially overflowing from Integer.MAX_VALUE to 0 instead of Integer.MIN_VALUE)

As a final note, this problem seems to be known for some time. See for example this entry about the corresponding findbugs rule.

shawkinaw
  • 3,190
  • 2
  • 27
  • 30
bernard paulus
  • 1,644
  • 1
  • 21
  • 33
14

Here is what Java doc says for Math.abs() in javadoc:

Note that if the argument is equal to the value of Integer.MIN_VALUE, the most negative representable int value, the result is that same value, which is negative.

moe
  • 28,814
  • 4
  • 19
  • 16
8

To see the result that you are expecting, cast Integer.MIN_VALUE to long:

System.out.println(Math.abs((long) Integer.MIN_VALUE));
Bombe
  • 81,643
  • 20
  • 123
  • 127
  • 1
    A possible fix, indeed! However, this does not solve the fact that `Math.abs` is being counterintuitive by returning a negative number: `Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE` – bernard paulus Jun 17 '13 at 09:38
  • 1
    @bernardpaulus, well, what is it supposed to do, besides throwing an `ArithmeticException`? Also, the behaviour is clearly documented in the API documentation. – Bombe Jun 21 '13 at 08:39
  • there is no good answer to your question... I just wanted to point out that this behaviour, which is a source of bugs, is not fixed by the use of `Math.abs(long)`. I apologise for my mistake here: I tought that you proposed the use of `Math.abs(long)` as a fix, when you showed it as a simple way to "see the result the asker is expecting". Sorry. – bernard paulus Jun 22 '13 at 23:19
  • In Java 15 with the new methods in fact a Exception is thrown. – chiperortiz Apr 20 '20 at 14:59
4

There is a fix to this in Java 15 will be a method to int and long. They will be present on the classes

java.lang.Math and java.lang.StrictMath

The methods.

public static int absExact(int a)
public static long absExact(long a)

If you pass

Integer.MIN_VALUE

OR

Long.MIN_VALUE

A Exception is thrown.

https://bugs.openjdk.java.net/browse/JDK-8241805

I would like to see if either Long.MIN_VALUE or Integer.MIN_VALUE is passed a positive value would be return and not a exception but.

chiperortiz
  • 4,751
  • 9
  • 45
  • 79
1

2147483648 cannot be stored in an integer in java, its binary representation is the same as -2147483648.

ymajoros
  • 2,454
  • 3
  • 34
  • 60
0

But (int) 2147483648L == -2147483648 There is one negative number which has no positive equivalent so there is not positive value for it. You will see the same behaviour with Long.MAX_VALUE.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
-3

Math.abs doesn't work all the time with big numbers I use this little code logic that I learnt when I was 7 years old!

if(Num < 0){
  Num = -(Num);
} 
Dave
  • 109
  • 8