8240632: Note differences between IEEE 754-2019 math lib special cases and java.lang.Math

Reviewed-by: bpb
This commit is contained in:
Joe Darcy 2021-02-08 21:50:55 +00:00
parent ace8f94616
commit 2fd8ed024b
5 changed files with 264 additions and 21 deletions
src/java.base/share/classes/java/lang
test/jdk/java/lang/Math

@ -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 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
* 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 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
* 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 final class Math {
/**
* 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 final class Math {
* 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 final class Math {
* 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 final class Math {
* <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 final class Math {
* <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 final class Math {
* 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 final class Math {
* <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 final class Math {
* <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 final class Math {
* <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

@ -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 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
* 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 @@ public final class 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 @@ public final class 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 @@ public final class 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 final class StrictMath {
* <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 final class StrictMath {
* <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 final class StrictMath {
* 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 final class StrictMath {
* <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 final class StrictMath {
* 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 final class StrictMath {
* <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

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011,2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2020, 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
@ -23,7 +23,7 @@
/*
* @test
* @bug 8255368
* @bug 8255368 8240632
* @summary Tests corner cases of Math.exp
*/
@ -46,6 +46,10 @@ public class ExpCornerCaseTests {
double [][] testCases = {
{+0x4.0p8, Double.POSITIVE_INFINITY},
{+0x2.71p12, Double.POSITIVE_INFINITY},
// Identified special cases in IEEE 754 exp operation
{+0.0, 1.0},
{-0.0, 1.0},
};
for (double[] testCase : testCases) {

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 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
@ -26,7 +26,7 @@
* @library /test/lib
* @build jdk.test.lib.RandomFactory
* @run main HypotTests
* @bug 4851638 4939441 8078672
* @bug 4851638 4939441 8078672 8240632
* @summary Tests for {Math, StrictMath}.hypot (use -Dseed=X to set PRNG seed)
* @author Joseph D. Darcy
* @key randomness
@ -198,6 +198,13 @@ public class HypotTests {
return failures;
}
/**
* Verify +0.0 is returned if both arguments are zero.
*/
private static int testHypotZeros() {
return testHypotCase(0.0, 0.0, +0.0, 0.0);
}
static int testHypotCase(double input1, double input2, double expected) {
return testHypotCase(input1,input2, expected, 1);
}
@ -237,6 +244,7 @@ public class HypotTests {
int failures = 0;
failures += testHypot();
failures += testHypotZeros();
if (failures > 0) {
System.err.println("Testing the hypot incurred "

@ -0,0 +1,130 @@
/*
* Copyright (c) 2011, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8240632
* @summary Test special cases of IEEE 754 recommended ops not otherwise tested
* @build Tests
* @build Ieee754SpecialCaseTests
* @run main Ieee754SpecialCaseTests
*/
public class Ieee754SpecialCaseTests {
private Ieee754SpecialCaseTests() {throw new AssertionError("No instances for you.");}
public static void main(String... args) {
int failures = 0;
failures += testSpecialCos();
failures += testSpecialAcos();
failures += testSpecialAtan();
failures += testSpecialLog();
if (failures > 0) {
System.err.printf("Testing special cases incurred %d failures.%n", failures);
throw new RuntimeException();
}
}
private static int testSpecialCos() {
int failures = 0;
double [][] testCases = {
{+0.0, 1.0},
{-0.0, 1.0},
};
for(double[] testCase: testCases) {
failures += testCosCase(testCase[0], testCase[1]);
}
return failures;
}
private static int testCosCase(double input, double expected) {
int failures = 0;
failures += Tests.test("Math.cos", input, Math.cos(input), expected);
failures += Tests.test("StrictMath.cos", input, StrictMath.cos(input), expected);
return failures;
}
private static int testSpecialAcos() {
int failures = 0;
double [][] testCases = {
{1.0, 0.0},
};
for(double[] testCase: testCases) {
failures += testAcosCase(testCase[0], testCase[1]);
}
return failures;
}
private static int testAcosCase(double input, double expected) {
int failures = 0;
failures += Tests.test("Math.acos", input, Math.acos(input), expected);
failures += Tests.test("StrictMath.acos", input, StrictMath.acos(input), expected);
return failures;
}
private static int testSpecialAtan() {
int failures = 0;
double [][] testCases = {
{Double.POSITIVE_INFINITY, +Math.PI/2.0},
{Double.NEGATIVE_INFINITY, -Math.PI/2.0},
};
for(double[] testCase: testCases) {
failures += testAtanCase(testCase[0], testCase[1]);
}
return failures;
}
private static int testAtanCase(double input, double expected) {
int failures = 0;
failures += Tests.test("Math.atan", input, Math.atan(input), expected);
failures += Tests.test("StrictMath.atan", input, StrictMath.atan(input), expected);
return failures;
}
private static int testSpecialLog() {
int failures = 0;
double [][] testCases = {
{1.0, +0.0},
};
for(double[] testCase: testCases) {
failures += testLogCase(testCase[0], testCase[1]);
}
return failures;
}
private static int testLogCase(double input, double expected) {
int failures = 0;
failures += Tests.test("Math.log", input, Math.log(input), expected);
failures += Tests.test("StrictMath.log", input, StrictMath.log(input), expected);
return failures;
}
}