JDK-8261116 : Note differences between IEEE 754-2019 math lib special cases and java.lang.Math
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 17
  • Submitted: 2021-02-04
  • Updated: 2021-02-08
  • Resolved: 2021-02-08
Related Reports
CSR :  
Relates :  
Relates :  
Description
Summary
-------

Add explicit specifications of behavior on cardinal values to math library methods as appropriate to align with IEEE 754 recommendations. 

Problem
-------

The 2019 IEEE 754 revision includes guidance on the behavior of math library methods (sin, cos, tan, etc.) on cardinal values. The Java math library should as much as possible align with these recommendations and note any differences.

Solution
--------

Update the `java.lang.Math` and `java.lang.StrictMath` specifications to align with IEEE 754 recommendation where possible, noting differences.

Specification
-------------
On a per-method level, the same changes are made both in the `Math` and `StrictMath` classes:


    --- a/src/java.base/share/classes/java/lang/Math.java
    +++ b/src/java.base/share/classes/java/lang/Math.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -37,7 +37,7 @@
      * square root, and trigonometric functions.
      *
      * <p>Unlike some of the numeric methods of class
    - * {@code StrictMath}, all implementations of the equivalent
    + * {@link java.lang.StrictMath StrictMath}, all implementations of the equivalent
      * functions of class {@code Math} are not defined to return the
      * bit-for-bit same results.  This relaxation permits
      * better-performing implementations where strict reproducibility is
    @@ -99,6 +99,28 @@
      * occurs only with a specific minimum or maximum value and
      * should be checked against the minimum or maximum as appropriate.
      *
    + * <h2><a id=Ieee754RecommendedOps>IEEE 754 Recommended
    + * Operations</a></h2>
    + *
    + * The 2019 revision of the IEEE 754 floating-point standard includes
    + * a section of recommended operations and the semantics of those
    + * operations if they are included in a programming environment. The
    + * recommended operations present in this class include {@link sin
    + * sin}, {@link cos cos}, {@link tan tan}, {@link asin asin}, {@link
    + * acos acos}, {@link atan atan}, {@link exp exp}, {@link expm1
    + * expm1}, {@link log log}, {@link log10 log10}, {@link log1p log1p},
    + * {@link sinh sinh}, {@link cosh cosh}, {@link tanh tanh}, {@link
    + * hypot hypot}, and {@link pow pow}.  (The {@link sqrt sqrt}
    + * operation is a required part of IEEE 754 from a different section
    + * of the standard.) The special case behavior of the recommended
    + * operations generally follows the guidance of the IEEE 754
    + * standard. However, the {@code pow} method defines different
    + * behavior for some arguments, as noted in its {@linkplain pow
    + * specification}. The IEEE 754 standard defines its operations to be
    + * correctly rounded, which is a more stringent quality of
    + * implementation condition than required for most of the methods in
    + * question that are also included in this class.
    + *
      * @author  Joseph D. Darcy
      * @since   1.0
      */
    @@ -156,7 +178,9 @@ public static double sin(double a) {
         /**
          * Returns the trigonometric cosine of an angle. Special cases:
          * <ul><li>If the argument is NaN or an infinity, then the
    -     * result is NaN.</ul>
    +     * result is NaN.
    +     * <li>If the argument is zero, then the result is {@code 1.0}.
    +     *</ul>
          *
          * <p>The computed result must be within 1 ulp of the exact result.
          * Results must be semi-monotonic.
    @@ -209,7 +233,9 @@ public static double asin(double a) {
          * Returns the arc cosine of a value; the returned angle is in the
          * range 0.0 through <i>pi</i>.  Special case:
          * <ul><li>If the argument is NaN or its absolute value is greater
    -     * than 1, then the result is NaN.</ul>
    +     * than 1, then the result is NaN.
    +     * <li>If the argument is {@code 1.0}, the result is positive zero.
    +     * </ul>
          *
          * <p>The computed result must be within 1 ulp of the exact result.
          * Results must be semi-monotonic.
    @@ -226,7 +252,11 @@ public static double acos(double a) {
          * range -<i>pi</i>/2 through <i>pi</i>/2.  Special cases:
          * <ul><li>If the argument is NaN, then the result is NaN.
          * <li>If the argument is zero, then the result is a zero with the
    -     * same sign as the argument.</ul>
    +     * same sign as the argument.
    +     * <li>If the argument is {@linkplain Double#isInfinite infinite},
    +     * then the result is the closest value to <i>pi</i>/2 with the
    +     * same sign as the input.
    +     * </ul>
          *
          * <p>The computed result must be within 1 ulp of the exact result.
          * Results must be semi-monotonic.
    @@ -275,7 +305,9 @@ public static double toDegrees(double angrad) {
          * <li>If the argument is positive infinity, then the result is
          * positive infinity.
          * <li>If the argument is negative infinity, then the result is
    -     * positive zero.</ul>
    +     * positive zero.
    +     * <li>If the argument is zero, then the result is {@code 1.0}.
    +     * </ul>
          *
          * <p>The computed result must be within 1 ulp of the exact result.
          * Results must be semi-monotonic.
    @@ -297,7 +329,10 @@ public static double exp(double a) {
          * <li>If the argument is positive infinity, then the result is
          * positive infinity.
          * <li>If the argument is positive zero or negative zero, then the
    -     * result is negative infinity.</ul>
    +     * result is negative infinity.
    +     * <li>If the argument is {@code 1.0}, then the result is positive
    +     * zero.
    +     * </ul>
          *
          * <p>The computed result must be within 1 ulp of the exact result.
          * Results must be semi-monotonic.
    @@ -321,8 +356,10 @@ public static double log(double a) {
          * positive infinity.
          * <li>If the argument is positive zero or negative zero, then the
          * result is negative infinity.
    -     * <li> If the argument is equal to 10<sup><i>n</i></sup> for
    -     * integer <i>n</i>, then the result is <i>n</i>.
    +     * <li>If the argument is equal to 10<sup><i>n</i></sup> for
    +     * integer <i>n</i>, then the result is <i>n</i>. In particular,
    +     * if the argument is {@code 1.0} (10<sup>0</sup>), then the
    +     * result is positive zero.
          * </ul>
          *
          * <p>The computed result must be within 1 ulp of the exact result.
    @@ -529,6 +566,15 @@ public static double rint(double a) {
          * <p>The computed result must be within 2 ulps of the exact result.
          * Results must be semi-monotonic.
          *
    +     * @apiNote
    +     * For <i>y</i> with a positive sign and finite nonzero
    +     * <i>x</i>, the exact mathematical value of {@code atan2} is
    +     * equal to:
    +     * <ul>
    +     * <li>If <i>x</i> {@literal >} 0, atan(abs(<i>y</i>/<i>x</i>))
    +     * <li>If <i>x</i> {@literal <} 0, &pi; - atan(abs(<i>y</i>/<i>x</i>))
    +     * </ul>
    +     *
          * @param   y   the ordinate coordinate
          * @param   x   the abscissa coordinate
          * @return  the <i>theta</i> component of the point
    @@ -660,6 +706,16 @@ public static double atan2(double y, double x) {
          * <p>The computed result must be within 1 ulp of the exact result.
          * Results must be semi-monotonic.
          *
    +     * @apiNote
    +     * The special cases definitions of this method differ from the
    +     * special case definitions of the IEEE 754 recommended {@code
    +     * pow} operation for &plusmn;{@code 1.0} raised to an infinite
    +     * power. This method treats such cases as indeterminate and
    +     * specifies a NaN is returned. The IEEE 754 specification treats
    +     * the infinite power as a large integer (large-magnitude
    +     * floating-point numbers are numerically integers, specifically
    +     * even integers) and therefore specifies {@code 1.0} be returned.
    +     *
          * @param   a   the base.
          * @param   b   the exponent.
          * @return  the value {@code a}<sup>{@code b}</sup>.
    @@ -2113,6 +2169,7 @@ public static double tanh(double x) {
          * <li> If either argument is NaN and neither argument is infinite,
          * then the result is NaN.
          *
    +     * <li> If both arguments are zero, the result is positive zero.
          * </ul>
          *
          * <p>The computed result must be within 1 ulp of the exact
    diff --git a/src/java.base/share/classes/java/lang/StrictMath.java b/src/java.base/share/classes/java/lang/StrictMath.java
    index 941834381be..506324f3346 100644
    --- a/src/java.base/share/classes/java/lang/StrictMath.java
    +++ b/src/java.base/share/classes/java/lang/StrictMath.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
      * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      *
      * This code is free software; you can redistribute it and/or modify it
    @@ -74,6 +74,15 @@
      * occurs only with a specific minimum or maximum value and
      * should be checked against the minimum or maximum as appropriate.
      *
    + * <h2><a id=Ieee754RecommendedOps>IEEE 754 Recommended
    + * Operations</a></h2>
    + *
    + * The {@link java.lang.Math Math} class discusses how the shared
    + * quality of implementation criteria for selected {@code Math} and
    + * {@code StrictMath} methods <a
    + * href="Math.html#Ieee754RecommendedOps">relate to the IEEE 754
    + * recommended operations</a>.
    + *
      * @author  Joseph D. Darcy
      * @since   1.3
      */
    @@ -126,7 +135,9 @@ private StrictMath() {}
         /**
          * Returns the trigonometric cosine of an angle. Special cases:
          * <ul><li>If the argument is NaN or an infinity, then the
    -     * result is NaN.</ul>
    +     * result is NaN.
    +     * <li>If the argument is zero, then the result is {@code 1.0}.
    +     * </ul>
          *
          * @param   a   an angle, in radians.
          * @return  the cosine of the argument.
    @@ -162,7 +173,9 @@ private StrictMath() {}
          * Returns the arc cosine of a value; the returned angle is in the
          * range 0.0 through <i>pi</i>.  Special case:
          * <ul><li>If the argument is NaN or its absolute value is greater
    -     * than 1, then the result is NaN.</ul>
    +     * than 1, then the result is NaN.
    +     * <li>If the argument is {@code 1.0}, the result is positive zero.
    +     * </ul>
          *
          * @param   a   the value whose arc cosine is to be returned.
          * @return  the arc cosine of the argument.
    @@ -174,7 +187,11 @@ private StrictMath() {}
          * range -<i>pi</i>/2 through <i>pi</i>/2.  Special cases:
          * <ul><li>If the argument is NaN, then the result is NaN.
          * <li>If the argument is zero, then the result is a zero with the
    -     * same sign as the argument.</ul>
    +     * same sign as the argument.
    +     * <li>If the argument is {@linkplain Double#isInfinite infinite},
    +     * then the result is the closest value to <i>pi</i>/2 with the
    +     * same sign as the input.
    +     * </ul>
          *
          * @param   a   the value whose arc tangent is to be returned.
          * @return  the arc tangent of the argument.
    @@ -220,7 +237,9 @@ public static strictfp double toDegrees(double angrad) {
          * <li>If the argument is positive infinity, then the result is
          * positive infinity.
          * <li>If the argument is negative infinity, then the result is
    -     * positive zero.</ul>
    +     * positive zero.
    +     * <li>If the argument is zero, then the result is {@code 1.0}.
    +     * </ul>
          *
          * @param   a   the exponent to raise <i>e</i> to.
          * @return  the value <i>e</i><sup>{@code a}</sup>,
    @@ -238,7 +257,10 @@ public static double exp(double a) {
          * <li>If the argument is positive infinity, then the result is
          * positive infinity.
          * <li>If the argument is positive zero or negative zero, then the
    -     * result is negative infinity.</ul>
    +     * result is negative infinity.
    +     * <li>If the argument is {@code 1.0}, then the result is positive
    +     * zero.
    +     * </ul>
          *
          * @param   a   a value
          * @return  the value ln&nbsp;{@code a}, the natural logarithm of
    @@ -256,8 +278,10 @@ public static double exp(double a) {
          * positive infinity.
          * <li>If the argument is positive zero or negative zero, then the
          * result is negative infinity.
    -     * <li> If the argument is equal to 10<sup><i>n</i></sup> for
    -     * integer <i>n</i>, then the result is <i>n</i>.
    +     * <li>If the argument is equal to 10<sup><i>n</i></sup> for
    +     * integer <i>n</i>, then the result is <i>n</i>. In particular,
    +     * if the argument is {@code 1.0} (10<sup>0</sup>), then the
    +     * result is positive zero.
          * </ul>
          *
          * @param   a   a value
    @@ -517,6 +541,15 @@ public static double rint(double a) {
          * <li>If both arguments are negative infinity, then the result is the
          * {@code double} value closest to -3*<i>pi</i>/4.</ul>
          *
    +     * @apiNote
    +     * For <i>y</i> with a positive sign and finite nonzero
    +     * <i>x</i>, the exact mathematical value of {@code atan2} is
    +     * equal to:
    +     * <ul>
    +     * <li>If <i>x</i> {@literal >} 0, atan(abs(<i>y</i>/<i>x</i>))
    +     * <li>If <i>x</i> {@literal <} 0, &pi; - atan(abs(<i>y</i>/<i>x</i>))
    +     * </ul>
    +     *
          * @param   y   the ordinate coordinate
          * @param   x   the abscissa coordinate
          * @return  the <i>theta</i> component of the point
    @@ -642,6 +675,16 @@ public static double rint(double a) {
          * method if and only if the result of applying the method to the
          * value is equal to the value.)
          *
    +     * @apiNote
    +     * The special cases definitions of this method differ from the
    +     * special case definitions of the IEEE 754 recommended {@code
    +     * pow} operation for &plusmn;{@code 1.0} raised to an infinite
    +     * power. This method treats such cases as indeterminate and
    +     * specifies a NaN is returned. The IEEE 754 specification treats
    +     * the infinite power as a large integer (large-magnitude
    +     * floating-point numbers are numerically integers, specifically
    +     * even integers) and therefore specifies {@code 1.0} be returned.
    +     *
          * @param   a   base.
          * @param   b   the exponent.
          * @return  the value {@code a}<sup>{@code b}</sup>.
    @@ -1681,6 +1724,7 @@ public static float signum(float f) {
          * <li> If either argument is NaN and neither argument is infinite,
          * then the result is NaN.
          *
    +     * <li> If both arguments are zero, the result is positive zero.
          * </ul>
          *
          * @param x a value


Comments
Moving to Approved.
08-02-2021