< prev index next >

src/java.base/share/classes/java/lang/Math.java

Print this page

        

@@ -23,10 +23,11 @@
  * questions.
  */
 
 package java.lang;
 
+import java.math.BigDecimal;
 import java.util.Random;
 import jdk.internal.math.FloatConsts;
 import jdk.internal.math.DoubleConsts;
 import jdk.internal.HotSpotIntrinsicCandidate;
 

@@ -1448,10 +1449,173 @@
         }
         return (a <= b) ? a : b;
     }
 
     /**
+     * Returns the fused multiply-accumulate of the three arguments;
+     * that is, returns the exact product of the first two arguments
+     * summed with the third argument and then rounded once to the
+     * nearest {@code double}.
+     *
+     * The rounding is done using the {@linkplain
+     * java.math.RoundingMode#HALF_EVEN round to nearest even
+     * rounding mode}.
+     *
+     * In contrast, if {@code a * b + c} is evaluated as a regular
+     * floating-point expression, two rounding errors are involved,
+     * the first for the multiply operation, the second for the
+     * addition operation.
+     *
+     * <p>Special cases:
+     * <ul>
+     * <li> If any argument is NaN, the result is NaN.
+     *
+     * <li> If one of the first two arguments is infinite and the
+     * other is zero, the result is NaN.
+     *
+     * <li> If the exact product of the first two arguments is infinite
+     * (in other words, at least one of the arguments is infinite and
+     * the other is neither zero nor NaN) and the third argument is an
+     * infinity of the opposite sign, the result is NaN.
+     *
+     * </ul>
+     *
+     * <p>Note that {@code fusedMac(a, 1.0, c)} returns the same
+     * result as ({@code a + c}).  However,
+     * {@code fusedMac(a, b, +0.0)} does <em>not</em> always return the
+     * same result as ({@code a * b}) since
+     * {@code fusedMac(-0.0, +0.0, +0.0)} is {@code +0.0} while
+     * ({@code 0.0 * +0.0}) is {@code -0.0}; {@code fusedMac(a, b, -0.0)} is
+     * equivalent to ({@code a * b}) however.
+     *
+     * @param a a value
+     * @param b a value
+     * @param c a value
+     *
+     * @return (<i>a</i>&nbsp;&times;&nbsp;<i>b</i>&nbsp;+&nbsp;<i>c</i>)
+     * computed, as if with unlimited range and precision, and rounded
+     * once to the nearest {@code double} value
+     */
+    // @HotSpotIntrinsicCandidate
+    public static double fusedMac(double a, double b, double c) {
+        if (!Double.isFinite(a) ||
+            !Double.isFinite(b) ||
+            !Double.isFinite(c)) {
+            // Infinite and NaN arithmetic is not quite the same with
+            // two roundings as opposed to just one. With two
+            // roundings, the product and overflow and if the addend
+            // is infinite, a spurious NaN can be produced.
+            
+            if (Double.isNaN(a) || Double.isNaN(b) || Double.isNaN(c)) {
+                return Double.NaN;
+            } else { // One or more infinities in the input
+                if ((Double.isInfinite(a) && b == 0.0) ||
+                    (Double.isInfinite(b) && a == 0.0)) {
+                    return Double.NaN;
+                } else {
+                    double product = a * b;
+                    double result = product + c;
+                    if (Double.isInfinite(product) &&
+                        (!Double.isInfinite(a) && !Double.isInfinite(b))) {
+                        // Intermediate overflow; might cause a
+                        // spurious NaN with added to infinite c.
+                        return c;
+                    }
+                    return result;
+                }
+            }
+        } if (a == 1.0 || // Could just return b + c
+              b == 1.0 || // Could just return a + c
+              a == 0.0 || // Handle any signed zero issues
+              b == 0.0 || // Handle any signed zero issues
+              c == 0.0) { // Could be +0.0 or -0.0)
+            // The special cases of a == 1.0, b == 1.0, and c == 0.0
+            // are handled as double operations rather than falling
+            // back on BigDecimal. The case where a == 1.0 or b == 1.0
+            // would be handled correctly by the BigDecimal code
+            // below, unlike the signed zero case where the exact
+            // product is -0.0 and c is -0.0.
+
+            // As noted above, if c is zero, it must still be added in
+            // since a negative zero can change the sign of the final
+            // result if the product is zero. At the cost of
+            // additional checks on a and b, the addition of a zero c
+            // could be avoided.
+
+            return a * b + c; 
+        } else {
+            // The set of values representable in BigDecimal includes
+            // neither signed zero nor infinities nor NaN. The logic
+            // above handles those cases. All finite double values are
+            // representable in BigDecimal. The code below exactly
+            // converts a, b, and c to their equivalent BigDecimal
+            // numbers, exactly computes the product and sum as
+            // appropriate, and then performs a single rounding to
+            // double using the round to nearest rounding mode.
+            return (((new BigDecimal(a)).multiply(new BigDecimal(b))).
+                    add(new BigDecimal(c))).doubleValue();
+        }
+    }
+
+    /**
+     * Returns the fused multiply-accumulate of the three arguments;
+     * that is, returns the exact product of the first two arguments
+     * summed with the third argument and then rounded once to the
+     * nearest {@code float}.
+     *
+     * The rounding is done using the {@linkplain
+     * java.math.RoundingMode#HALF_EVEN round to nearest even
+     * rounding mode}.
+     *
+     * In contrast, if {@code a * b + c} is evaluated as a regular
+     * floating-point expression, two rounding errors are involved,
+     * the first for the multiply operation, the second for the
+     * addition operation.
+     *
+     * <p>Special cases:
+     * <ul>
+     * <li> If any argument is NaN, the result is NaN.
+     *
+     * <li> If one of the first two arguments is infinite and the
+     * other is zero, the result is NaN.
+     *
+     * <li> If the exact product of the first two arguments is infinite
+     * (in other words, at least one of the arguments is infinite and
+     * the other is neither zero nor NaN) and the third argument is an
+     * infinity of the opposite sign, the result is NaN.
+     *
+     * </ul>
+     *
+     * <p>Note that {@code fusedMac(a, 1.0f, c)} returns the same
+     * result as ({@code a + c}).  However,
+     * {@code fusedMac(a, b, +0.0f)} does <em>not</em> always return the
+     * same result as ({@code a * b}) since
+     * {@code fusedMac(-0.0f, +0.0f, +0.0f)} is {@code +0.0f} while
+     * ({@code 0.0f * +0.0f}) is {@code -0.0f}; {@code fusedMac(a, b, -0.0f)} is
+     * equivalent to ({@code a * b}) however.
+     *
+     * @param a a value
+     * @param b a value
+     * @param c a value
+     *
+     * @return (<i>a</i>&nbsp;&times;&nbsp;<i>b</i>&nbsp;+&nbsp;<i>c</i>)
+     * computed, as if with unlimited range and precision, and rounded
+     * once to the nearest {@code float} value
+     */
+    // @HotSpotIntrinsicCandidate
+    public static float fusedMac(float a, float b, float c) {
+        // Since double has more than twice the precision of float,
+        // the multiply of a * b is exact in double. The add of c to
+        // the product then has one rounded error. Since double
+        // moreover has more than 2p + 2 precision compared to float,
+        // the double rounding of (a*b + c) from the double format to
+        // the float format is equivalent to rounding the result
+        // directly to float precision.
+        return (float)(((double) a * (double) b ) + (double) c);
+    }
+
+    /**
      * Returns the size of an ulp of the argument.  An ulp, unit in
      * the last place, of a {@code double} value is the positive
      * distance between this floating-point value and the {@code
      * double} value next larger in magnitude.  Note that for non-NaN
      * <i>x</i>, <code>ulp(-<i>x</i>) == ulp(<i>x</i>)</code>.
< prev index next >