2
\$\begingroup\$

I have to write an API for checked subtraction where the input minuend can be < 0 but subtrahend can't be < 0. I think I've covered all the cases of underflow and overflow. With given conditions the range of subtrahend is [Long.MIN_VALUE, Long.MAX_VALUE] and of minuend is [0, Long.MAX_VALUE]; with these limits overflow is not possible and underflow can happen if subtrahend > minuend and in that case setting Long.MIN_VALUE as the minimum possible difference seems legit.

public static long checkedSubtract(final long minuend, final long subtrahend) {
    Preconditions.checkArgument(subtrahend >= 0, "Subtrahend must be >= 0");

    long difference;
    try {
        difference = LongMath.checkedSubtract(minuend, subtrahend);
    } catch (ArithmeticException e) {
        // With the precondition of subtrahend >= 0, this operation can not
        // overflow and if it underflows, return minimum possible difference 
        difference = Long.MIN_VALUE;
    }
    return difference;
}

I'd appreciate it if someone could affirm my logic and/or point out any mistake.

\$\endgroup\$
2
\$\begingroup\$

Incorporating exceptions to handle expected situations is never a good idea. Creating an exception instance in Java is significantly slower than the if condition that would prevent the exception. Exceptions should be exceptions.

Additionally, as maaartinus says, you should not ned to declare the difference outside the try block. Even if you accept the exception as valid, your code would be simpler as:

try {
    return LongMath.checkedSubtract(minuend, subtrahend);
} catch (ArithmeticException e) {
    // With the precondition of subtrahend >= 0, this operation can not
    // overflow and if it underflows, return minimum possible difference 
    return Long.MIN_VALUE;
}

Regardless, you give no indication where the LongMath class comes from. Still, your situation would be much simpler as:

public static long checkedSubtract(final long  minuend, final long subtrahend) {
    Preconditions.checkArgument(subtrahend >= 0, "Subtrahend must be >= 0");
    long result = minuend - subtrahend;
    // if the result underflows, it will be larger than the start balance
    return result > minuend ? Long.MIN_VALUE : result;
}
\$\endgroup\$
  • \$\begingroup\$ Actually, is this wrong? Overflow happens, if minuend < Long.MIN_VALUE + subtrahend. But I guess, what you are doing is better, because difference is computed only once. \$\endgroup\$ – user1071840 Jan 10 '15 at 3:09
4
\$\begingroup\$

I guess your logic is fine. I just don't like what you do with the variable difference. You could return directly instead and save the variable and two lines.

In case the overflow s probable, you're wasting a lot of time because of the exception. You could do something like

long result = minuend - subtrahend;
return minuend >= 0 | (minuend ^ result) >= 0 ? result : Long.MIN_VALUE;

instead (condition adapted from LongMath.checkedSubtract). You could test it both against your original implementation (e.g. with a few random values) and against some known cases.


As suggested by user1071840, a simpler condition may be used:

return Long.MIN_VALUE + subtrahend > minuend ? minuend - subtrahend : Long.MIN_VALUE;

Actually, rolfl's condition is even simpler.

\$\endgroup\$
  • \$\begingroup\$ @user1071840 I don't see why your expression should be right. Maybe it is, can you show it? \$\endgroup\$ – maaartinus Jan 10 '15 at 3:00
  • \$\begingroup\$ Ouch, I forgot to replace the variable names. \$\endgroup\$ – user1071840 Jan 10 '15 at 3:04
  • \$\begingroup\$ if (Long.MIN_VALUE + subtrahend > minuend) { return Long.MIN_VALUE; } else { return minuend - subtrahend; } \$\endgroup\$ – user1071840 Jan 10 '15 at 3:05
  • \$\begingroup\$ Overflow will happen, if (minuend - subtrahend) < Long.MIN_VALUE, which means minuend < Long.MIN_VALUE + subtrahend. Unless, I've made a mistake in the signs with inequalities. \$\endgroup\$ – user1071840 Jan 10 '15 at 3:07
  • \$\begingroup\$ @user1071840 This makes sense. My more complicated condition is a direct simplification of LongMath.checkedSubtract. \$\endgroup\$ – maaartinus Jan 10 '15 at 3:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.