jdk-24/test/jdk/java/lang/StrictMath/TrigTests.java
2024-03-01 19:30:35 +00:00

332 lines
12 KiB
Java

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