Search code examples
javafloating-pointintegerinteger-overflow

How to detect and prevent integer overflow when multiplying an integer by float in Java?


I have read this article NUM00-J. Detect or prevent integer overflow and this question How does Java handle integer underflows and overflows and how would you check for it?.

As you can see, there are many solutions to prevent integer overflow when multiplying an integer by an integer. But I wonder is there any solution to prevent integer overflow when multiplying an integer by float?

My current (silly) solution:

public static final int mulInt(int a, float b) {
    double c = a * b;
    return c > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)c;
}

But it has a lot of problems:

  1. It can get the expected result when multiplying performs multiplication with both parameters having to be small numbers.
  2. When either parameter is large digits the result is bound to be incorrect (I know in part because of the floating-point data type).
  3. Suppose if the result of the calculation is even greater than the maximum value of double, it will be unstoppable and return a negative number.

So, what is the real solution to this problem?

Your answer will be very helpful, I will appreciate it!

UPDATE: There is another question here How can I check if multiplying two numbers in Java will cause an overflow? that is quite similar BUT it is about multiplying an integer by an integer instead of multiplying by a float.


Solution

  • Below is a C approach that may shed light in Java.

    Perform the multiplication using double, not float math before the assginment to gain the extra precision/range of double. Overflow is not then expected.

    A compare like c > Integer.MAX_VALUE suffers from Integer.MAX_VALUE first being converted into a double. This may lose precision.*1 Consider what happens if the converted value is Integer.MAX_VALUE + 1.0. Then if c is Integer.MAX_VALUE + 1.0, code will attempt to return (int) (Integer.MAX_VALUE + 1.0) - not good. Better to use well formed limits. (Negative ones too.) In C, maybe Java, floating point conversion to int truncates the fraction. Special care is needed near the edges.

    #define INT_MAX_PLUS1_AS_DOUBLE ((INT_MAX/2 + 1)*2.0)
    
    int mulInt(int a, float b) {
      // double c = a * b;
      double c = (double) a * b;
      
      //return c > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)c;
      if (c < INT_MAX_PLUS1_AS_DOUBLE && c - INT_MIN > -1.0) {
        return (int) c;
      } 
      if (c > 0) return INT_MAX;
      if (c < 0) return INT_MIN;
      return 0; // `b` was a NaN
    }
    

    c - INT_MIN > -1 is like c > INT_MIN - 1, but as INT_MIN is a -power-of-2, INT_MIN - 1 might not convert precisely to double. c - INT_MIN is expected to be exact near the edge cases.


    *1 When int is 32-bit (or less) and double is 64-bit (with 53-bit significand) not an issue. But important with wider integer types.