From fcaf871408321ed523cf1c6dd3adf9914f2bf9aa Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Wed, 22 Feb 2023 22:49:59 +0000 Subject: [PATCH] 8302028: Port fdlibm atan2 to Java Reviewed-by: bpb --- .../share/classes/java/lang/FdLibm.java | 110 +++++++++ .../share/classes/java/lang/StrictMath.java | 4 +- test/jdk/java/lang/Math/Atan2Tests.java | 226 ++++++++++++++++-- test/jdk/java/lang/StrictMath/Atan2Tests.java | 191 +++++++++++++++ .../java/lang/StrictMath/ExhaustingTests.java | 4 +- .../java/lang/StrictMath/FdlibmTranslit.java | 108 +++++++++ 6 files changed, 615 insertions(+), 28 deletions(-) create mode 100644 test/jdk/java/lang/StrictMath/Atan2Tests.java diff --git a/src/java.base/share/classes/java/lang/FdLibm.java b/src/java.base/share/classes/java/lang/FdLibm.java index d925cd322e1..334fee9a8c6 100644 --- a/src/java.base/share/classes/java/lang/FdLibm.java +++ b/src/java.base/share/classes/java/lang/FdLibm.java @@ -394,6 +394,116 @@ class FdLibm { } } + /** + * Returns the angle theta from the conversion of rectangular + * coordinates (x, y) to polar coordinates (r, theta). + * + * Method : + * 1. Reduce y to positive by atan2(y,x)=-atan2(-y,x). + * 2. Reduce x to positive by (if x and y are unexceptional): + * ARG (x+iy) = arctan(y/x) ... if x > 0, + * ARG (x+iy) = pi - arctan[y/(-x)] ... if x < 0, + * + * Special cases: + * + * ATAN2((anything), NaN ) is NaN; + * ATAN2(NAN , (anything) ) is NaN; + * ATAN2(+-0, +(anything but NaN)) is +-0 ; + * ATAN2(+-0, -(anything but NaN)) is +-pi ; + * ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2; + * ATAN2(+-(anything but INF and NaN), +INF) is +-0 ; + * ATAN2(+-(anything but INF and NaN), -INF) is +-pi; + * ATAN2(+-INF,+INF ) is +-pi/4 ; + * ATAN2(+-INF,-INF ) is +-3pi/4; + * ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2; + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + static class Atan2 { + private Atan2() {throw new UnsupportedOperationException();} + + private static final double + tiny = 1.0e-300, + pi_o_4 = 0x1.921fb54442d18p-1, // 7.8539816339744827900E-01 + pi_o_2 = 0x1.921fb54442d18p0, // 1.5707963267948965580E+00 + pi_lo = 0x1.1a62633145c07p-53; // 1.2246467991473531772E-16 + + static double compute(double y, double x) { + double z; + int k, m, hx, hy, ix, iy; + /*unsigned*/ int lx, ly; + + hx = __HI(x); + ix = hx & 0x7fff_ffff; + lx = __LO(x); + hy = __HI(y); + iy = hy&0x7fff_ffff; + ly = __LO(y); + if (Double.isNaN(x) || Double.isNaN(y)) + return x + y; + if (((hx - 0x3ff0_0000) | lx) == 0) // x = 1.0 + return StrictMath.atan(y); + m = ((hy >> 31) & 1)|((hx >> 30) & 2); // 2*sign(x) + sign(y) + + // when y = 0 + if ((iy | ly) == 0) { + switch(m) { + case 0: + case 1: return y; // atan(+/-0, +anything) = +/-0 + case 2: return Math.PI + tiny; // atan(+0, -anything) = pi + case 3: return -Math.PI - tiny; // atan(-0, -anything) = -pi + } + } + // when x = 0 + if ((ix | lx) == 0) { + return (hy < 0)? -pi_o_2 - tiny : pi_o_2 + tiny; + } + + // when x is INF + if (ix == 0x7ff0_0000) { + if (iy == 0x7ff0_0000) { + switch(m) { + case 0: return pi_o_4 + tiny; // atan(+INF, +INF) + case 1: return -pi_o_4 - tiny; // atan(-INF, +INF) + case 2: return 3.0*pi_o_4 + tiny; // atan(+INF, -INF) + case 3: return -3.0*pi_o_4 - tiny; // atan(-INF, -INF) + } + } else { + switch(m) { + case 0: return 0.0; // atan(+..., +INF) + case 1: return -0.0; // atan(-..., +INF) + case 2: return Math.PI + tiny; // atan(+..., -INF) + case 3: return -Math.PI - tiny; // atan(-..., -INF) + } + } + } + // when y is INF + if (iy == 0x7ff0_0000) { + return (hy < 0)? -pi_o_2 - tiny : pi_o_2 + tiny; + } + + // compute y/x + k = (iy - ix) >> 20; + if (k > 60) { // |y/x| > 2**60 + z = pi_o_2+0.5*pi_lo; + } else if (hx < 0 && k < -60) { // |y|/x < -2**60 + z = 0.0; + } else { // safe to do y/x + z = StrictMath.atan(Math.abs(y/x)); + } + switch (m) { + case 0: return z; // atan(+, +) + case 1: return -z; // atan(-, +) + case 2: return Math.PI - (z - pi_lo); // atan(+, -) + default: return (z - pi_lo) - Math.PI; // atan(-, -), case 3 + } + } + } + /** * cbrt(x) * Return cube root of x diff --git a/src/java.base/share/classes/java/lang/StrictMath.java b/src/java.base/share/classes/java/lang/StrictMath.java index 740438b252d..d5831314d74 100644 --- a/src/java.base/share/classes/java/lang/StrictMath.java +++ b/src/java.base/share/classes/java/lang/StrictMath.java @@ -547,7 +547,9 @@ public final class StrictMath { * in polar coordinates that corresponds to the point * (xy) in Cartesian coordinates. */ - public static native double atan2(double y, double x); + public static double atan2(double y, double x) { + return FdLibm.Atan2.compute(y, x); + } /** * Returns the value of the first argument raised to the power of the diff --git a/test/jdk/java/lang/Math/Atan2Tests.java b/test/jdk/java/lang/Math/Atan2Tests.java index 7e5dca17f1b..c98012cf4e3 100644 --- a/test/jdk/java/lang/Math/Atan2Tests.java +++ b/test/jdk/java/lang/Math/Atan2Tests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2023, 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,36 +23,14 @@ /* * @test - * @bug 4984407 + * @bug 4984407 8302028 * @summary Tests for {Math, StrictMath}.atan2 */ public class Atan2Tests { private Atan2Tests(){} - static int testAtan2Case(double input1, double input2, double expected) { - int failures = 0; - failures += Tests.test("StrictMath.atan2", input1, input2, StrictMath::atan2, expected); - failures += Tests.test("Math.atan2", input1, input2, Math::atan2, expected); - - return failures; - } - - static int testAtan2() { - int failures = 0; - - double [][] testCases = { - {-3.0, Double.POSITIVE_INFINITY, -0.0}, - }; - - for (double[] testCase : testCases) { - failures+=testAtan2Case(testCase[0], testCase[1], testCase[2]); - } - - return failures; - } - - public static void main(String... argv) { + public static void main(String... args) { int failures = 0; failures += testAtan2(); @@ -63,4 +41,202 @@ public class Atan2Tests { throw new RuntimeException(); } } + + /** + * Special cases from the spec interspersed with test cases. + */ + private static int testAtan2() { + int failures = 0; + double NaNd = Double.NaN; + double MIN_VALUE = Double.MIN_VALUE; + double MIN_NORM = Double.MIN_NORMAL; + double MAX_VALUE = Double.MAX_VALUE; + double InfinityD = Double.POSITIVE_INFINITY; + double PI = Math.PI; + + /* + * If either argument is NaN, then the result is NaN. + */ + for(double nan : Tests.NaNs) { + failures += testAtan2Case(nan, 0.0, NaNd); + failures += testAtan2Case(0.0, nan, NaNd); + } + + double [][] testCases = { + /* + * If the first argument is positive zero and the second + * argument is positive, or the first argument is positive + * and finite and the second argument is positive + * infinity, then the result is positive zero. + */ + {+0.0, MIN_VALUE, +0.0}, + {+0.0, MIN_NORM, +0.0}, + {+0.0, 1.0, +0.0}, + {+0.0, MAX_VALUE, +0.0}, + {+0.0, InfinityD, +0.0}, + + {MIN_VALUE, InfinityD, +0.0}, + {MIN_NORM, InfinityD, +0.0}, + {1.0, InfinityD, +0.0}, + {MAX_VALUE, InfinityD, +0.0}, + {MIN_VALUE, InfinityD, +0.0}, + + /* + * If the first argument is negative zero and the second + * argument is positive, or the first argument is negative + * and finite and the second argument is positive + * infinity, then the result is negative zero. + */ + {-0.0, MIN_VALUE, -0.0}, + {-0.0, MIN_NORM, -0.0}, + {-0.0, 1.0, -0.0}, + {-0.0, MAX_VALUE, -0.0}, + {-0.0, InfinityD, -0.0}, + + {-MIN_VALUE, InfinityD, -0.0}, + {-MIN_NORM, InfinityD, -0.0}, + {-1.0, InfinityD, -0.0}, + {-MAX_VALUE, InfinityD, -0.0}, + + /* + * If the first argument is positive zero and the second + * argument is negative, or the first argument is positive + * and finite and the second argument is negative + * infinity, then the result is the double value closest + * to pi. + */ + {+0.0, -MIN_VALUE, PI}, + {+0.0, -MIN_NORM, PI}, + {+0.0, -1.0, PI}, + {+0.0, -MAX_VALUE, PI}, + {+0.0, -InfinityD, PI}, + + {MIN_VALUE, -InfinityD, PI}, + {MIN_NORM, -InfinityD, PI}, + {1.0, -InfinityD, PI}, + {MAX_VALUE, -InfinityD, PI}, + + /* + * If the first argument is negative zero and the second + * argument is negative, or the first argument is negative + * and finite and the second argument is negative + * infinity, then the result is the double value closest + * to -pi. + */ + {-0.0, -MIN_VALUE, -PI}, + {-0.0, -MIN_NORM, -PI}, + {-0.0, -1.0, -PI}, + {-0.0, -MAX_VALUE, -PI}, + {-0.0, -InfinityD, -PI}, + + {-MIN_VALUE, -InfinityD, -PI}, + {-MIN_NORM, -InfinityD, -PI}, + {-1.0, -InfinityD, -PI}, + {-MAX_VALUE, -InfinityD, -PI}, + + /* + * If the first argument is positive and the second + * argument is positive zero or negative zero, or the + * first argument is positive infinity and the second + * argument is finite, then the result is the double value + * closest to pi/2. + */ + {MIN_VALUE, +0.0, PI/2.0}, + {MIN_NORM, +0.0, PI/2.0}, + {1.0, +0.0, PI/2.0}, + {MAX_VALUE, +0.0, PI/2.0}, + + {MIN_VALUE, -0.0, PI/2.0}, + {MIN_VALUE, -0.0, PI/2.0}, + {MIN_NORM, -0.0, PI/2.0}, + {1.0, -0.0, PI/2.0}, + {MAX_VALUE, -0.0, PI/2.0}, + + {InfinityD, -MIN_VALUE, PI/2.0}, + {InfinityD, -MIN_NORM, PI/2.0}, + {InfinityD, -1.0, PI/2.0}, + {InfinityD, -MAX_VALUE, PI/2.0}, + + {InfinityD, MIN_VALUE, PI/2.0}, + {InfinityD, MIN_NORM, PI/2.0}, + {InfinityD, 1.0, PI/2.0}, + {InfinityD, MAX_VALUE, PI/2.0}, + + /* + * If the first argument is negative and the second argument is + * positive zero or negative zero, or the first argument is + * negative infinity and the second argument is finite, then the + * result is the double value closest to -pi/2. + */ + {-MIN_VALUE, +0.0, -PI/2.0}, + {-MIN_NORM, +0.0, -PI/2.0}, + {-1.0, +0.0, -PI/2.0}, + {-MAX_VALUE, +0.0, -PI/2.0}, + + {-MIN_VALUE, -0.0, -PI/2.0}, + {-MIN_VALUE, -0.0, -PI/2.0}, + {-MIN_NORM, -0.0, -PI/2.0}, + {-1.0, -0.0, -PI/2.0}, + {-MAX_VALUE, -0.0, -PI/2.0}, + + {-InfinityD, -MIN_VALUE, -PI/2.0}, + {-InfinityD, -MIN_NORM, -PI/2.0}, + {-InfinityD, -1.0, -PI/2.0}, + {-InfinityD, -MAX_VALUE, -PI/2.0}, + + {-InfinityD, MIN_VALUE, -PI/2.0}, + {-InfinityD, MIN_NORM, -PI/2.0}, + {-InfinityD, 1.0, -PI/2.0}, + {-InfinityD, MAX_VALUE, -PI/2.0}, + + /* + * If both arguments are positive infinity, then the result is the + * double value closest to pi/4. + */ + {InfinityD, InfinityD, PI/4.0}, + + /* + * If the first argument is positive infinity and the + * second argument is negative infinity, then the result + * is the double value closest to 3*pi/4. + */ + // Note: in terms of computation, the result of the double + // expression + // 3*PI/4.0 + // is the same as a high-precision decimal value of pi + // scaled accordingly and rounded to double: + // BigDecimal bdPi = new BigDecimal("3.14159265358979323846264338327950288419716939937510"); + // bdPi.multiply(BigDecimal.valueOf(3)).divide(BigDecimal.valueOf(4)).doubleValue(); + {InfinityD, -InfinityD, 3*PI/4.0}, + + /* + * If the first argument is negative infinity and the second + * argument is positive infinity, then the result is the double + * value closest to -pi/4. + */ + {-InfinityD, InfinityD, -PI/4.0}, + + /* + * If both arguments are negative infinity, then the result is the + * double value closest to -3*pi/4. + */ + {-InfinityD, -InfinityD, -3*PI/4.0}, + + {-3.0, InfinityD, -0.0}, + }; + + for (double[] testCase : testCases) { + failures += testAtan2Case(testCase[0], testCase[1], testCase[2]); + } + + return failures; + } + + private static int testAtan2Case(double input1, double input2, double expected) { + int failures = 0; + failures += Tests.test("StrictMath.atan2", input1, input2, StrictMath::atan2, expected); + failures += Tests.test("Math.atan2", input1, input2, Math::atan2, expected); + + return failures; + } } diff --git a/test/jdk/java/lang/StrictMath/Atan2Tests.java b/test/jdk/java/lang/StrictMath/Atan2Tests.java new file mode 100644 index 00000000000..4ef76b5ed66 --- /dev/null +++ b/test/jdk/java/lang/StrictMath/Atan2Tests.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2003, 2023, 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 4851638 + * @key randomness + * @summary Tests for StrictMath.atan2 + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @build Tests + * @build FdlibmTranslit + * @build Atan2Tests + * @run main Atan2Tests + */ + +import jdk.test.lib.RandomFactory; + +/** + * The tests in ../Math/Atan2Tests.java test properties that should + * hold for any atan2 implementation, including the FDLIBM-based one + * required for StrictMath.atan2. Therefore, the test cases in + * ../Math/Atan2Tests.java are run against both the Math and + * StrictMath versions of atan2. The role of this test is to verify + * that the FDLIBM atan2 algorithm is being used by running golden + * file tests on values that may vary from one conforming atan2 + * implementation to another. + */ + +public class Atan2Tests { + private Atan2Tests(){} + + public static void main(String... args) { + int failures = 0; + + failures += testAgainstTranslit(); + + if (failures > 0) { + System.err.println("Testing atan2 incurred " + + failures + " failures."); + throw new RuntimeException(); + } + } + + // Initialize shared random number generator + private static java.util.Random random = RandomFactory.getRandom(); + + /** + * Test StrictMath.atan2 against transliteration port of atan2. + */ + private static int testAgainstTranslit() { + int failures = 0; + + double MIN_VALUE = Double.MIN_VALUE; + double MIN_NORM = Double.MIN_NORMAL; + double MAX_VALUE = Double.MAX_VALUE; + double InfinityD = Double.POSITIVE_INFINITY; + double PI = Math.PI; + + // The exact special cases for infinity, NaN, zero, + // etc. inputs are checked in the Math tests. + + // Test exotic NaN bit patterns + double[][] exoticNaNs = { + {Double.longBitsToDouble(0x7FF0_0000_0000_0001L), 0.0}, + {0.0, Double.longBitsToDouble(0x7FF0_0000_0000_0001L)}, + {Double.longBitsToDouble(0xFFF_00000_0000_0001L), 0.0}, + {0.0, Double.longBitsToDouble(0xFFF0_0000_0000_0001L)}, + {Double.longBitsToDouble(0x7FF_00000_7FFF_FFFFL), 0.0}, + {0.0, Double.longBitsToDouble(0x7FF0_7FFF_0000_FFFFL)}, + {Double.longBitsToDouble(0xFFF_00000_7FFF_FFFFL), 0.0}, + {0.0, Double.longBitsToDouble(0xFFF0_7FFF_0000_FFFFL)}, + }; + + for (double[] exoticNaN: exoticNaNs) { + failures += testAtan2Case(exoticNaN[0], exoticNaN[1], + FdlibmTranslit.atan2(exoticNaN[0], exoticNaN[1])); + } + + // Probe near decision points in the FDLIBM algorithm. + double[][] decisionPoints = { + // If x == 1, return atan(y) + {0.5, Math.nextDown(1.0)}, + {0.5, 1.0}, + {0.5, Math.nextUp(1.0)}, + + { MIN_VALUE, MIN_VALUE}, + { MIN_VALUE, -MIN_VALUE}, + {-MIN_VALUE, MIN_VALUE}, + {-MIN_VALUE, -MIN_VALUE}, + + { MAX_VALUE, MAX_VALUE}, + { MAX_VALUE, -MAX_VALUE}, + {-MAX_VALUE, MAX_VALUE}, + {-MAX_VALUE, -MAX_VALUE}, + + { MIN_VALUE, MAX_VALUE}, + { MAX_VALUE, MIN_VALUE}, + + {-MIN_VALUE, MAX_VALUE}, + {-MAX_VALUE, MIN_VALUE}, + + {MIN_VALUE, -MAX_VALUE}, + {MAX_VALUE, -MIN_VALUE}, + + {-MIN_VALUE, -MAX_VALUE}, + {-MAX_VALUE, -MIN_VALUE}, + }; + + for (double[] decisionPoint: decisionPoints) { + failures += testAtan2Case(decisionPoint[0], decisionPoint[1], + FdlibmTranslit.atan2(decisionPoint[0], decisionPoint[1])); + } + + // atan2 looks at the ratio y/x and executes different code + // paths accordingly: tests for 2^60 and 2^-60. + + double y = 1.0; + double x = 0x1.0p60; + double increment_x = Math.ulp(x); + double increment_y = Math.ulp(y); + y = y - 128*increment_y; + x = x - 128*increment_x; + + for (int i = 0; i < 256; i++, x += increment_x) { + for (int j = 0; j < 256; j++, y += increment_y) { + failures += testAtan2Case( y, x, FdlibmTranslit.atan2( y, x)); + failures += testAtan2Case(-y, x, FdlibmTranslit.atan2(-y, x)); + failures += testAtan2Case( y, -x, FdlibmTranslit.atan2( y, -x)); + failures += testAtan2Case(-y, -x, FdlibmTranslit.atan2(-y, -x)); + + failures += testAtan2Case( 2.0*y, 2.0*x, FdlibmTranslit.atan2( 2.0*y, 2.0*x)); + failures += testAtan2Case(-2.0*y, 2.0*x, FdlibmTranslit.atan2(-2.0*y, 2.0*x)); + failures += testAtan2Case( 2.0*y, -2.0*x, FdlibmTranslit.atan2( 2.0*y, -2.0*x)); + failures += testAtan2Case(-2.0*y, -2.0*x, FdlibmTranslit.atan2(-2.0*y, -2.0*x)); + + failures += testAtan2Case( 0.5*y, 0.5*x, FdlibmTranslit.atan2( 0.5*y, 0.5*x)); + failures += testAtan2Case(-0.5*y, 0.5*x, FdlibmTranslit.atan2(-0.5*y, 0.5*x)); + failures += testAtan2Case( 0.5*y, -0.5*x, FdlibmTranslit.atan2( 0.5*y, -0.5*x)); + failures += testAtan2Case(-0.5*y, -0.5*x, FdlibmTranslit.atan2(-0.5*y, -0.5*x)); + + // Switch argument position + failures += testAtan2Case( x, y, FdlibmTranslit.atan2( x, y)); + failures += testAtan2Case(-x, y, FdlibmTranslit.atan2(-x, y)); + failures += testAtan2Case( x, -y, FdlibmTranslit.atan2( x, -y)); + failures += testAtan2Case(-x, -y, FdlibmTranslit.atan2(-x, -y)); + + failures += testAtan2Case( 0.5*x, 0.5*y, FdlibmTranslit.atan2( 0.5*x, 0.5*y)); + failures += testAtan2Case(-0.5*x, 0.5*y, FdlibmTranslit.atan2(-0.5*x, 0.5*y)); + failures += testAtan2Case( 0.5*x, -0.5*y, FdlibmTranslit.atan2( 0.5*x, -0.5*y)); + failures += testAtan2Case(-0.5*x, -0.5*y, FdlibmTranslit.atan2(-0.5*x, -0.5*y)); + } + } + + // Check random values + for (int k = 0; k < 200; k++ ) { + y = random.nextDouble(); + x = random.nextDouble(); + failures += testAtan2Case(y, x, FdlibmTranslit.atan2(y, x)); + } + + return failures; + } + + private static int testAtan2Case(double input1, double input2, double expected) { + int failures = 0; + failures += Tests.test("StrictMath.atan2(double)", input1, input2, + StrictMath::atan2, expected); + return failures; + } +} diff --git a/test/jdk/java/lang/StrictMath/ExhaustingTests.java b/test/jdk/java/lang/StrictMath/ExhaustingTests.java index c34a910aaf9..6b90e8f115a 100644 --- a/test/jdk/java/lang/StrictMath/ExhaustingTests.java +++ b/test/jdk/java/lang/StrictMath/ExhaustingTests.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8301833 8302026 8301444 + * @bug 8301833 8302026 8301444 8302028 * @build Tests * @build FdlibmTranslit * @build ExhaustingTests @@ -130,7 +130,7 @@ public class ExhaustingTests { // probes). BinaryTestCase[] testCases = { new BinaryTestCase("hypot", FdlibmTranslit::hypot, StrictMath::hypot, 20, 20), - // new BinaryTestCase("atan2", FdlibmTranslit::atan2, StrictMath::atan2, 20, 20), + new BinaryTestCase("atan2", FdlibmTranslit::atan2, StrictMath::atan2, 20, 20), }; for (var testCase : testCases) { diff --git a/test/jdk/java/lang/StrictMath/FdlibmTranslit.java b/test/jdk/java/lang/StrictMath/FdlibmTranslit.java index 2a9f5d2ca0a..916000da68b 100644 --- a/test/jdk/java/lang/StrictMath/FdlibmTranslit.java +++ b/test/jdk/java/lang/StrictMath/FdlibmTranslit.java @@ -82,6 +82,10 @@ public class FdlibmTranslit { return Atan.compute(x); } + public static double atan2(double y, double x) { + return Atan2.compute(y, x); + } + public static double hypot(double x, double y) { return Hypot.compute(x, y); } @@ -396,6 +400,110 @@ public class FdlibmTranslit { } } + /** + * Returns the angle theta from the conversion of rectangular + * coordinates (x, y) to polar coordinates (r, theta). + * + * Method : + * 1. Reduce y to positive by atan2(y,x)=-atan2(-y,x). + * 2. Reduce x to positive by (if x and y are unexceptional): + * ARG (x+iy) = arctan(y/x) ... if x > 0, + * ARG (x+iy) = pi - arctan[y/(-x)] ... if x < 0, + * + * Special cases: + * + * ATAN2((anything), NaN ) is NaN; + * ATAN2(NAN , (anything) ) is NaN; + * ATAN2(+-0, +(anything but NaN)) is +-0 ; + * ATAN2(+-0, -(anything but NaN)) is +-pi ; + * ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2; + * ATAN2(+-(anything but INF and NaN), +INF) is +-0 ; + * ATAN2(+-(anything but INF and NaN), -INF) is +-pi; + * ATAN2(+-INF,+INF ) is +-pi/4 ; + * ATAN2(+-INF,-INF ) is +-3pi/4; + * ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2; + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + static class Atan2 { + private static final double + tiny = 1.0e-300, + zero = 0.0, + pi_o_4 = 7.8539816339744827900E-01, /* 0x3FE921FB, 0x54442D18 */ + pi_o_2 = 1.5707963267948965580E+00, /* 0x3FF921FB, 0x54442D18 */ + pi = 3.1415926535897931160E+00, /* 0x400921FB, 0x54442D18 */ + pi_lo = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */ + + static double compute(double y, double x) { + double z; + int k,m,hx,hy,ix,iy; + /*unsigned*/ int lx,ly; + + hx = __HI(x); ix = hx&0x7fffffff; + lx = __LO(x); + hy = __HI(y); iy = hy&0x7fffffff; + ly = __LO(y); + if(((ix|((lx|-lx)>>>31))>0x7ff00000)|| // Note unsigned shifts + ((iy|((ly|-ly)>>>31))>0x7ff00000)) /* x or y is NaN */ + return x+y; + if(((hx-0x3ff00000)|lx)==0) return atan(y); /* x=1.0 */ + m = ((hy>>31)&1)|((hx>>30)&2); /* 2*sign(x)+sign(y) */ + + /* when y = 0 */ + if((iy|ly)==0) { + switch(m) { + case 0: + case 1: return y; /* atan(+-0,+anything)=+-0 */ + case 2: return pi+tiny;/* atan(+0,-anything) = pi */ + case 3: return -pi-tiny;/* atan(-0,-anything) =-pi */ + } + } + /* when x = 0 */ + if((ix|lx)==0) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* when x is INF */ + if(ix==0x7ff00000) { + if(iy==0x7ff00000) { + switch(m) { + case 0: return pi_o_4+tiny;/* atan(+INF,+INF) */ + case 1: return -pi_o_4-tiny;/* atan(-INF,+INF) */ + case 2: return 3.0*pi_o_4+tiny;/*atan(+INF,-INF)*/ + case 3: return -3.0*pi_o_4-tiny;/*atan(-INF,-INF)*/ + } + } else { + switch(m) { + case 0: return zero ; /* atan(+...,+INF) */ + case 1: return -1.0*zero ; /* atan(-...,+INF) */ + case 2: return pi+tiny ; /* atan(+...,-INF) */ + case 3: return -pi-tiny ; /* atan(-...,-INF) */ + } + } + } + /* when y is INF */ + if(iy==0x7ff00000) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* compute y/x */ + k = (iy-ix)>>20; + if(k > 60) z=pi_o_2+0.5*pi_lo; /* |y/x| > 2**60 */ + else if(hx<0&&k<-60) z=0.0; /* |y|/x < -2**60 */ + else z=atan(Math.abs(y/x)); /* safe to do y/x */ + switch (m) { + case 0: return z ; /* atan(+,+) */ + case 1: + // original:__HI(z) ^= 0x80000000; + z = __HI(z, __HI(z) ^ 0x80000000); + return z ; /* atan(-,+) */ + case 2: return pi-(z-pi_lo);/* atan(+,-) */ + default: /* case 3 */ + return (z-pi_lo)-pi;/* atan(-,-) */ + } + } + } + /** * cbrt(x) * Return cube root of x