20

Java 8 gave us Math.addExact() for integers but not decimals.

Is it possible for double and BigDecimal to overflow? Judging by Double.MAX_VALUE and How to get biggest BigDecimal value I'd say the answer is yes.

As such, why don't we have Math.addExact() for those types as well? What's the most maintainable way to check this ourselves?

Community
  • 1
  • 1
Gili
  • 86,244
  • 97
  • 390
  • 689
  • 2
    Anything of a finite precision can, theoretically, overflow. – David W Aug 12 '15 at 20:34
  • 5
    Supporting addExact for double is a lot harder because it is an approximate representation in the first place. You can't even add 0.1 and 0.2 exactly. The overflow for BigDecimal is so large you are likely to run out of memory first. – Peter Lawrey Aug 12 '15 at 20:34
  • 2
    If with doubles you go too far that it cannot longer be represented you get an `Infinity` value. That would be an overflow. – Edwin Dalorzo Aug 12 '15 at 20:35
  • @EdwinDalorzo What if an `Infinity` is already one of the inputs? It's a valid input for floating-point arithmetic. – rgettman Aug 12 '15 at 20:36
  • 1
    Double can overflow but it does not wrap around like ordinal types do. What floating point can do, which integer types absolutely can't is **underflow**: a result that is so close to zero that it must be rounded off to zero. `BigDecimal` can be as big as your memory can hold (more or less), so though it can theoretically overflow, in practice it won't. Not unless you're calculating the values of the Ackermann function :) – biziclop Aug 12 '15 at 20:38

3 Answers3

17

double overflows to Infinity and -Infinity, it doesn't wrap around. BigDecimal doesn't overflow, period, it is only limited by the amount of memory in your computer. See: How to get biggest BigDecimal value

The only difference between + and .addExact is that it attempts to detect if overflow has occurred and throws an Exception instead of wraps. Here's the source code:

public static int addExact(int x, int y) {
    int r = x + y;
    // HD 2-12 Overflow iff both arguments have the opposite sign of the result
    if (((x ^ r) & (y ^ r)) < 0) {
        throw new ArithmeticException("integer overflow");
    }
    return r;
}

If you want to check that an overflow has occurred, in one sense it's simpler to do it with double anyway because you can simply check for Double.POSITIVE_INFINITY or Double.NEGATIVE_INFINITY; in the case of int and long, it's a slightly more complicated matter because it isn't always one fixed value, but in another, these could be inputs (e.g. Infinity + 10 = Infinity and you probably don't want to throw an exception in this case).

For all these reasons (and we haven't even mentioned NaN yet), this is probably why such an addExact method doesn't exist in the JDK. Of course, you can always add your own implementation to a utility class in your own application.

Community
  • 1
  • 1
durron597
  • 31,968
  • 17
  • 99
  • 158
  • 1
    So to do the same for double you'd replace the if conditions with `if (r == Double.POSITIVE_INFINITY || r == Double.NEGATIVE_INFINITY)`? – Gili Aug 12 '15 at 20:37
  • Regarding `BigDecimal`, in theory you're right. In practice, there is an implementation limit: http://stackoverflow.com/a/6792114/14731. – Gili Aug 12 '15 at 20:38
  • @Gili Not quite, cause you'd want to allow that result if either operand were either infinity -- and what would you do if you added POSITIVE_INFINITY and NEGATIVE_INFINITY (which results in NaN, not either infinity)? Should that count as "overflow"? Basically, doubles have more complicated behavior, and so are trickier to neatly bucket into "reasonable" and "unreasonable" results. – yshavit Aug 12 '15 at 20:38
  • 1
    And that doesn't take into account ULP weirdness -- the fact that if you have a double larger than 2^53, and you add 1.0 to it, the result is the original number -- since the smallest distance between two doubles at that size is > 1.0. Should_that_ count as an ArithmeticException? Cause if that does, then a whole lot of similar things will, in ways you probably won't expect (even at small/reasonable values). – yshavit Aug 12 '15 at 20:44
  • @yshavit That's a great point, but I won't update my answer with it because it's not really an "overflow". – durron597 Aug 12 '15 at 20:45
  • I would do the following: if either input is `Double.isInfinite()` throw `IllegalArgumentException`. If the result of addition is `Double.isInfinite()` throw `ArithmeticException`. – Gili Aug 12 '15 at 20:46
  • @Gili Sure, I mean, do whatever you want in your own app – durron597 Aug 12 '15 at 20:47
  • @yshavit The previous comment about `Double.isInfinite()` was meant for you. Also, good point about the ULP weirdness. – Gili Aug 12 '15 at 20:47
  • 1
    @durron597 Yeah, my point wasn't so much that it's overflow, but that with ints there's one _specific_ kind of weirdness (ie, overflow) so it's pretty easy to come up with a clear method whose semantics are "do the addition, and throw an exception if you hit that one weirdness; if I don't get an exception, I'll have an intuitive result" With doubles, pretty much the whole thing is weirdness, so you can't really have that kind of helper method. – yshavit Aug 12 '15 at 21:41
5

The reason you do not need a addExact function for floating point digits is because instead of wrapping around, it overflows to Double.Infinity.

Consequently you can very easily check at the end of the operation whether it overflowed or not. Since Double.POSITIVE_INFINITY + Double.NEGATIVE_INFINITY is NaN you also have to check for NaN in case of more complicated expressions.

This is not only faster but also easier to read. Instead of having Math.addExact(Math.addExact(x, y), z) to add 3 doubles together, you can instead write:

double result = x + y + z;
if (Double.isInfinite(result) || Double.isNan(result)) throw ArithmeticException("overflow");

BigDecimal on the other hand will indeed overflow and throw a corresponding exception in that case as well - this is very unlikely to ever happen in practice though.

Voo
  • 29,040
  • 11
  • 82
  • 156
  • 2
    ...which is made possible by the fact that operations on `Infinity` are well defined, so `Infinity + any number apart from -Infinity` will still be `Infinity`. You can execute any series of operations, if it overflows at an intermediate point, you'll never get a false but valid result: it will always be either one of the infinities or `NaN`. This is in contrast with integer types, where this can happen. – biziclop Aug 12 '15 at 20:43
  • @biziclop True to be accurate you'd have to check for `NaN` as well in case of more complicated expressions. – Voo Aug 12 '15 at 20:48
3

For double, please check the other answers.

BigDecimal has the addExact() protection already built in. Many arithmetic operation methods (e.g. multiply) of BigDecimal contain a check on the scale of the result:

private int checkScale(long val) {
    int asInt = (int)val;
    if (asInt != val) {
        asInt = val>Integer.MAX_VALUE ? Integer.MAX_VALUE : Integer.MIN_VALUE;
        BigInteger b;
        if (intCompact != 0 &&
            ((b = intVal) == null || b.signum() != 0))
            throw new ArithmeticException(asInt>0 ? "Underflow":"Overflow");
    }
    return asInt;
}
Glorfindel
  • 21,988
  • 13
  • 81
  • 109