/* * Copyright (c) 2003, 2024, 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. */ import jdk.test.lib.RandomFactory; import java.util.function.DoubleUnaryOperator; /* * @test * @bug 8302026 * @key randomness * @library /test/lib * @build jdk.test.lib.RandomFactory * @build Tests * @build FdlibmTranslit * @build InverseTrigTests * @run main InverseTrigTests * @summary Tests for StrictMath.{asin, acos, atan} */ /** * The tests in ../Math/InverseTrigTests.java test properties that * should hold for any implementation of the inverse trig functions * ason, acos, and atan, including the FDLIBM-based ones required by * the StrictMath class. Therefore, the test cases in * ../Math/InverseTrig.java are run against both the Math and * StrictMath versions of the inverse trig methods. The role of this * test is to verify that the FDLIBM algorithms are being used by * running golden file tests on values that may vary from one * conforming implementation of the hyperbolics to another. */ public class InverseTrigTests { private InverseTrigTests(){} public static void main(String... args) { int failures = 0; failures += testAgainstTranslitCommon(); failures += testAsin(); failures += testAcos(); failures += testAtan(); if (failures > 0) { System.err.println("Testing the inverse trig functions incurred " + failures + " failures."); throw new RuntimeException(); } } /** * Bundle together groups of testing methods. */ private static enum InverseTrigTest { ASIN(InverseTrigTests::testAsinCase, FdlibmTranslit::asin), ACOS(InverseTrigTests::testAcosCase, FdlibmTranslit::acos), ATAN(InverseTrigTests::testAtanCase, FdlibmTranslit::atan); private DoubleDoubleToInt testCase; private DoubleUnaryOperator transliteration; InverseTrigTest(DoubleDoubleToInt testCase, DoubleUnaryOperator transliteration) { this.testCase = testCase; this.transliteration = transliteration; } public DoubleDoubleToInt testCase() {return testCase;} public DoubleUnaryOperator transliteration() {return transliteration;} } // Initialize shared random number generator private static java.util.Random random = RandomFactory.getRandom(); /** * Test against shared points of interest. */ private static int testAgainstTranslitCommon() { int failures = 0; double[] pointsOfInterest = { Double.MIN_NORMAL, 1.0, Tests.createRandomDouble(random), }; for (var testMethods : InverseTrigTest.values()) { for (double testPoint : pointsOfInterest) { failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, testMethods); } } return failures; } /** * Test StrictMath.asin against transliteration port of asin. */ private static int testAsin() { int failures = 0; // Probe near decision points in the FDLIBM algorithm. double[] decisionPoints = { 0x1p-27, -0x1p-27, 0.5, -0.5, 0.975, -0.975, }; for (double testPoint : decisionPoints) { failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, InverseTrigTest.ASIN); } // Empirical worst-case points in other libraries with larger // worst-case error than FDLIBM double [][] testCases = { {-0x1.00d44cccfa99p-1, -0x1.0d0a6a0e79e15p-1}, {-0x1.0239000439deep-1, -0x1.0ea71ea2a7cd7p-1}, {+0x1.0479b37d95e5cp-1, 0x1.1143fafdc5b2cp-1}, {-0x1.2ef2481799c7cp-1, -0x1.442d10aa50906p-1}, }; for (double[] testCase : testCases) { failures += testAsinCase(testCase[0], testCase[1]); } return failures; } /** * Test StrictMath.acos against transliteration port of acos. */ private static int testAcos() { int failures = 0; // Probe near decision points in the FDLIBM algorithm. double[] decisionPoints = { 0.5, -0.5, 0x1.0p-57, -0x1.0p-57, }; for (double testPoint : decisionPoints) { failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, InverseTrigTest.ACOS); } // Empirical worst-case points in other libraries with larger // worst-case error than FDLIBM double [][] testCases = { {+0x1.35b03e336a82bp-1, 0x1.d7a84ec2f6707p-1}, {-0x1.8d313198a2e03p-53, 0x1.921fb54442d19p0}, {-0x1.010fd0ad6aa41p-1, 0x1.0c63a8cd23befp1}, {+0x1.251869c3f7881p-1, 0x1.ec2ff0c102684p-1}, {+0x1.266637a3d2bbcp-1, 0x1.ea98637533648p-1}, }; for (double[] testCase : testCases) { failures += testAcosCase(testCase[0], testCase[1]); } return failures; } /** * Test StrictMath.atan against transliteration port of atan */ private static int testAtan() { int failures = 0; // Probe near decision points in the FDLIBM algorithm. double[] decisionPoints = { 0.0, 7.0/16.0, 11.0/16.0, 19.0/16.0, 39.0/16.0, 0x1.0p66, 0x1.0p-29, }; for (double testPoint : decisionPoints) { failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, InverseTrigTest.ATAN); } // Empirical worst-case points in other libraries with larger // worst-case error than FDLIBM double [][] testCases = { {+0x1.0103fc4ebaaa8p+1, 0x1.1bd5c375c97b5p0}, {+0x1.01e0be37af68fp+1, 0x1.1c2d45ec82765p0}, {-0x1.60370d15396b7p-1, -0x1.348461c347fd9p-1}, {+0x1.032b4811f3dc5p+0, 0x1.9545fd233ee14p-1}, {+0x1.52184b1b9bd9bp+0, 0x1.d86de33c93814p-1}, {-0x1.0684fa9fa7481p+0, -0x1.988f9da70b11ap-1}, }; for (double[] testCase : testCases) { failures += testAtanCase(testCase[0], testCase[1]); } return failures; } private interface DoubleDoubleToInt { int apply(double x, double y); } private static int testRange(double start, double increment, int count, InverseTrigTest testMethods) { int failures = 0; double x = start; for (int i = 0; i < count; i++, x += increment) { failures += testMethods.testCase().apply(x, testMethods.transliteration().applyAsDouble(x)); } return failures; } private static int testRangeMidpoint(double midpoint, double increment, int count, InverseTrigTest testMethods) { int failures = 0; double x = midpoint - increment*(count / 2) ; for (int i = 0; i < count; i++, x += increment) { failures += testMethods.testCase().apply(x, testMethods.transliteration().applyAsDouble(x)); } return failures; } private static int testAsinCase(double input, double expected) { return Tests.test("StrictMath.asin(double)", input, StrictMath::asin, expected); } private static int testAcosCase(double input, double expected) { return Tests.test("StrictMath.acos(double)", input, StrictMath::acos, expected); } private static int testAtanCase(double input, double expected) { return Tests.test("StrictMath.atan(double)", input, StrictMath::atan, expected); } }