/* * Copyright (c) 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 8301833 8302026 8301444 8302028 8302040 8302027 8304028 * @build Tests * @build FdlibmTranslit * @build ExhaustingTests * @run main ExhaustingTests * @summary Compare StrictMath.foo and FdlibmTranslit.foo for many inputs. */ /* * Note on usage: for more exhaustive testing to help validate changes * to StrictMath, the DEFAULT_SHIFT setting should be set to 0. This * will test all float values against the unary methods. Running all * the float values for a single method takes on the order of a minute * or two. The default setting is a shift of 10, meaning every 1024th * float value is tested and the overall test runs within the typical * time expectations of a tier 1 test. */ import java.util.function.DoubleBinaryOperator; import java.util.function.DoubleUnaryOperator; public class ExhaustingTests { public static void main(String... args) { long failures = 0; failures += testUnaryMethods(); failures += testBinaryMethods(); if (failures > 0) { System.err.println("Comparing StrictMath and FdlibmTranslit" + " incurred " + failures + " failures."); throw new RuntimeException(); } } private static final int DEFAULT_SHIFT = 10; /** * Test the unary (one-argument) StrictMath methods from FDLIBM. */ private static long testUnaryMethods() { long failures = 0; UnaryTestCase[] testCases = { // Since sqrt is correctly rounded and thus for each input // there is one well-defined correct result, additional // comparison of the transliteration sqrt or StrictMath // sqrt could be made against Math::sqrt. new UnaryTestCase("sqrt", FdlibmTranslit::sqrt, StrictMath::sqrt, DEFAULT_SHIFT), new UnaryTestCase("cbrt", FdlibmTranslit::cbrt, StrictMath::cbrt, DEFAULT_SHIFT), new UnaryTestCase("log", FdlibmTranslit::log, StrictMath::log, DEFAULT_SHIFT), new UnaryTestCase("log10", FdlibmTranslit::log10, StrictMath::log10, DEFAULT_SHIFT), new UnaryTestCase("log1p", FdlibmTranslit::log1p, StrictMath::log1p, DEFAULT_SHIFT), new UnaryTestCase("exp", FdlibmTranslit::exp, StrictMath::exp, DEFAULT_SHIFT), new UnaryTestCase("expm1", FdlibmTranslit::expm1, StrictMath::expm1, DEFAULT_SHIFT), new UnaryTestCase("sinh", FdlibmTranslit::sinh, StrictMath::sinh, DEFAULT_SHIFT), new UnaryTestCase("cosh", FdlibmTranslit::cosh, StrictMath::cosh, DEFAULT_SHIFT), new UnaryTestCase("tanh", FdlibmTranslit::tanh, StrictMath::tanh, DEFAULT_SHIFT), new UnaryTestCase("sin", FdlibmTranslit::sin, StrictMath::sin, DEFAULT_SHIFT), new UnaryTestCase("cos", FdlibmTranslit::cos, StrictMath::cos, DEFAULT_SHIFT), new UnaryTestCase("tan", FdlibmTranslit::tan, StrictMath::tan, DEFAULT_SHIFT), new UnaryTestCase("asin", FdlibmTranslit::asin, StrictMath::asin, DEFAULT_SHIFT), new UnaryTestCase("acos", FdlibmTranslit::acos, StrictMath::acos, DEFAULT_SHIFT), new UnaryTestCase("atan", FdlibmTranslit::atan, StrictMath::atan, DEFAULT_SHIFT), }; for (var testCase : testCases) { System.out.println("Testing " + testCase.name()); System.out.flush(); int i = Integer.MAX_VALUE; // overflow to Integer.MIN_VALUE at start of loop int increment = 1 << testCase.shiftDistance; do { i += increment; double input = (double)Float.intBitsToFloat(i); failures += Tests.test(testCase.name(), input, testCase.strictMath, testCase.translit.applyAsDouble(input)); } while (i != Integer.MAX_VALUE); } return failures; } private static record UnaryTestCase(String name, DoubleUnaryOperator translit, DoubleUnaryOperator strictMath, int shiftDistance) { UnaryTestCase { if (shiftDistance < 0 || shiftDistance >= 31) { throw new IllegalArgumentException("Shift out of range"); } } } /** * Test the binary (two-argument) StrictMath methods from FDLIBM. */ private static long testBinaryMethods() { long failures = 0; // Note: pow does _not_ have a transliteration port. // Shift of 16 for a binary method gives comparable running // time to exhaustive testing of a unary method (testing every // 2^16 floating point values over two arguments is 2^32 // probes). BinaryTestCase[] testCases = { new BinaryTestCase("hypot", FdlibmTranslit::hypot, StrictMath::hypot, 20, 20), new BinaryTestCase("atan2", FdlibmTranslit::atan2, StrictMath::atan2, 20, 20), new BinaryTestCase("IEEEremainder", FdlibmTranslit::IEEEremainder, StrictMath::IEEEremainder, 20, 20), }; for (var testCase : testCases) { System.out.println("Testing " + testCase.name()); System.out.flush(); int iIncrement = 1 << testCase.xShift; int jIncrement = 1 << testCase.yShift; for (long i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i += iIncrement) { for (long j = Integer.MIN_VALUE; j <= Integer.MAX_VALUE; j += jIncrement) { double input1 = (double)Float.intBitsToFloat((int)i); double input2 = (double)Float.intBitsToFloat((int)j); failures += Tests.test(testCase.name(), input1, input2, testCase.strictMath, testCase.translit.applyAsDouble(input1, input2)); } } } return failures; } private static record BinaryTestCase(String name, DoubleBinaryOperator translit, DoubleBinaryOperator strictMath, int xShift, int yShift) { BinaryTestCase { if (xShift < 0 || xShift >= 31 || yShift < 0 || yShift >= 31 ) { throw new IllegalArgumentException("Shift out of range"); } } } }