| /* | |
| * 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); | |
| } | |
| } |