/* * 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 8302027 * @key randomness * @library /test/lib * @build jdk.test.lib.RandomFactory * @build Tests * @build FdlibmTranslit * @build TrigTests * @run main TrigTests * @summary Tests for StrictMath.{sin, cos, tan} */ /** * The tests in ../Math/{TanTests.java, SinCosTests.java} test * properties that should hold for any implementation of the trig * functions sin, cos, and tan, including the FDLIBM-based ones * required by the StrictMath class. Therefore, the test cases in * ../Math/{TanTests.java, SinCosTests.java} are run against both the * Math and StrictMath versions of the 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 trig functions to another. */ public class TrigTests { private TrigTests(){} public static void main(String... args) { int failures = 0; failures += testAgainstTranslitCommon(); failures += testAgainstTranslitSin(); failures += testAgainstTranslitCos(); failures += testAgainstTranslitTan(); if (failures > 0) { System.err.println("Testing the trig functions incurred " + failures + " failures."); throw new RuntimeException(); } } /** * Bundle together groups of testing methods. */ private static enum TrigTest { SIN(TrigTests::testSinCase, FdlibmTranslit::sin), COS(TrigTests::testCosCase, FdlibmTranslit::cos), TAN(TrigTests::testTanCase, FdlibmTranslit::tan); private DoubleDoubleToInt testCase; private DoubleUnaryOperator transliteration; TrigTest(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 = { Math.PI/4.0, -Math.PI/4.0, Math.PI/2.0, -Math.PI/2.0, 3.0*Math.PI/2.0, -3.0*Math.PI/2.0, Math.PI, -Math.PI, 2.0*Math.PI, -2.0*Math.PI, Double.MIN_NORMAL, 1.0, Tests.createRandomDouble(random), }; for (var testMethods : TrigTest.values()) { for (double testPoint : pointsOfInterest) { failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, testMethods); } } return failures; } /** * Test StrictMath.sin against transliteration port of sin. */ private static int testAgainstTranslitSin() { int failures = 0; // Probe near decision points in the FDLIBM algorithm. double[] decisionPoints = { 0x1.0p-27, -0x1.0p-27, }; for (double testPoint : decisionPoints) { failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, TrigTest.SIN); } // Inputs where Math.sin and StrictMath.sin differ for at least // one Math.sin implementation. double [][] testCases = { {0x1.00000006eeeefp-12, 0x1.ffffffb888889p-13}, {0x1.00000006eeefp-12, 0x1.ffffffb88888bp-13}, {0x1.00000006eeef1p-12, 0x1.ffffffb88888dp-13}, {0x1.000000001bba2p-9, 0x1.ffffeaaae2633p-10}, {0x1.000000000013p-1, 0x1.eaee8744b0806p-2}, {0x1.0000000000012p0, 0x1.aed548f090d02p-1}, {0x1.00000000004e1p9, 0x1.45b52f29ac36p-4}, {0x1.00000000000cp10, -0x1.44ad26136ce5fp-3}, {0x1.000000000020bp11, -0x1.4092047afcd2p-2}, {0x1.0000000000003p12, -0x1.3074ea23314dep-1}, {0x1.0000000000174p50, -0x1.54cd5e7e9e3d2p-1}, {0x1.0000000000005p51, -0x1.8c35b0d728faep-2}, {0x1.0000000000101p113, -0x1.69e9ed300b1dcp-1}, {0x1.0000000000017p114, 0x1.f6b44aa2a1c9cp-1}, {0x1.00000000001abp128, -0x1.ecaddc1136bb2p-1}, {0x1.000000000001bp129, -0x1.682ccb977e4dp-1}, {0x1.0p233, 0x1.7c54e75ed6077p-1}, {0x1.00000000000fcp299, 0x1.78ad2fd7aef78p-1}, {0x1.0000000000002p300, -0x1.1adaf3550facp-1}, {0x1.00000000001afp1023, 0x1.d1c804ef2eeccp-1}, // Empirical worst-case points {-0x1.f8b791cafcdefp+4, -0x1.073ca87470dfap-3}, {-0x1.0e16eb809a35dp+944, 0x1.b5e361ed01dadp-2}, {-0x1.842d8ec8f752fp+21, -0x1.6ce864edeaffep-1}, {-0x1.1c49ad613ff3bp+19, -0x1.fffe203cfabe1p-2}, }; for (double[] testCase: testCases) { failures+=testSinCase(testCase[0], testCase[1]); } return failures; } /** * Test StrictMath.cos against transliteration port of cos. */ private static int testAgainstTranslitCos() { int failures = 0; // Probe near decision points in the FDLIBM algorithm. double[] decisionPoints = { 0x1.0p27, -0x1.0p27, 0.78125, -0.78125, }; for (double testPoint : decisionPoints) { failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, TrigTest.COS); } // Inputs where Math.cos and StrictMath.cos differ for at least // one Math.cos implementation. double [][] testCases = { {0x1.000000076aaa6p-10, 0x1.fffff00000147p-1}, {0x1.000000002e4fbp-8, 0x1.ffff00001554fp-1}, {0x1.0000000000318p-2, 0x1.f01549f7dee4p-1}, {0x1.000000000011ep-1, 0x1.c1528065b7cc6p-1}, {0x1.0000000000174p0, 0x1.14a280fb50419p-1}, {0x1.0000000000019p1, -0x1.aa226575372bbp-2}, {0x1.00000000018c9p9, -0x1.fe60f23b0016ap-1}, {0x1.0000000000022p10, 0x1.f98669d7b18d6p-1}, {0x1.0000000000281p11, 0x1.e6439428b217p-1}, {0x1.0000000000001p12, 0x1.9ba4a85e6e173p-1}, {0x1.0000000000211p20, 0x1.e33ad93554beep-1}, {0x1.0000000000006p21, 0x1.9027223f77694p-1}, {0x1.00000000000b8p95, 0x1.8315138968a66p-1}, {0x1.0000000000043p96, 0x1.5b302d1c86cbcp-4}, {0x1.000000000013ap127, -0x1.740d46d7821f4p-1}, {0x1.0000000000002p128, -0x1.e050345cf2161p-1}, {0x1.000000000014p299, 0x1.6c5f3c84352fep-1}, {0x1.0000000000007p300, -0x1.55109bfdf1c5cp-1}, {0x1.000000000010ep400, 0x1.e725637029938p-2}, {0x1.0000000000007p401, 0x1.1f89e14e29ccep-1}, {0x1.0p402, 0x1.be2d53c4560dcp-1}, {0x1.000000000015fp1023, -0x1.2f2596c42735cp-1}, }; for (double[] testCase: testCases) { failures+=testCosCase(testCase[0], testCase[1]); } return failures; } /** * Test StrictMath.tan against transliteration port of tan */ private static int testAgainstTranslitTan() { int failures = 0; // Probe near decision points in the FDLIBM algorithm. double[] decisionPoints = { 0x1.0p-28, -0x1.0p-28, 0.6744, -0.6744, }; for (double testPoint : decisionPoints) { failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, TrigTest.TAN); } // Inputs where Math.tan and StrictMath.tan differ for at least // one Math.tan implementation. double [][] testCases = { {0x1.00000002221fep-13, 0x1.0000001777753p-13}, {0x1.0000000088859p-12, 0x1.00000055dddbp-12}, {0x1.0000000008787p-10, 0x1.000005555defep-10}, {0x1.0000000001423p-9, 0x1.0000155558b9ap-9}, {0x1.00000000005d9p-2, 0x1.05785a43c529p-2}, {0x1.000000000001fp-1, 0x1.17b4f5bf34772p-1}, {0x1.000000000006ep0, 0x1.8eb245cbee51ep0}, {0x1.0000000000032p1, -0x1.17af62e094fd7p1}, {0x1.00000000006a7p9, -0x1.46be0efd0f8cp-4}, {0x1.0p10, -0x1.48d5be43ada01p-3}, {0x1.00000000000c3p32, 0x1.0ad3757181cbap-1}, {0x1.0000000000005p33, 0x1.6e07fbf43d47p0}, {0x1.0000000000124p127, -0x1.3baa73a93958p0}, {0x1.000000000002p128, -0x1.bf05a77a8df0cp-1}, {0x1.000000000011cp299, 0x1.8a6f42eaa3d1fp0}, {0x1.000000000001cp300, -0x1.b30fc9f73002cp-1}, {0x1.0000000000013p500, -0x1.c4e46751be12cp-1}, {0x1.00000000000ep1023, -0x1.d52c4ec04f108p-2}, // Empirical worst-case points in other libraries with // larger worst-case errors than FDLIBM {+0x1.371a47b7e4eb2p+11, 0x1.9ded57c9ff46ap-1}, {-0x1.a81d98fc58537p+6 , 0x1.ffd83332326fdp-1}, {-0x1.13a5ccd87c9bbp+1008, -0x1.6a3815320e5cfp-1}, {-0x1.4d7c8b8320237p+11, -0x1.9dec1f1b36ecdp-1}, {+0x1.da7a85a88bbecp+11, 0x1.ff7ae7631230ep-1}, {-0x1.66af736e8555p+18, -0x1.fc3d1cb02536bp-1}, }; for (double[] testCase: testCases) { failures+=testTanCase(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, TrigTest 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, TrigTest 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 testSinCase(double input, double expected) { return Tests.test("StrictMath.sin(double)", input, StrictMath::sin, expected); } private static int testCosCase(double input, double expected) { return Tests.test("StrictMath.cos(double)", input, StrictMath::cos, expected); } private static int testTanCase(double input, double expected) { return Tests.test("StrictMath.tan(double)", input, StrictMath::tan, expected); } }