diff --git a/src/java.base/share/classes/java/lang/FdLibm.java b/src/java.base/share/classes/java/lang/FdLibm.java index 9526e7b695f..d925cd322e1 100644 --- a/src/java.base/share/classes/java/lang/FdLibm.java +++ b/src/java.base/share/classes/java/lang/FdLibm.java @@ -950,8 +950,9 @@ class FdLibm { * compiler will convert from decimal to binary accurately enough * to produce the hexadecimal values shown. */ - static class Exp { - private static final double one = 1.0; + static final class Exp { + private Exp() {throw new UnsupportedOperationException();} + private static final double[] half = {0.5, -0.5,}; private static final double huge = 1.0e+300; private static final double twom1000= 0x1.0p-1000; // 9.33263618503218878990e-302 = 2^-1000 @@ -969,10 +970,6 @@ class FdLibm { private static final double P4 = -0x1.bbd41c5d26bf1p-20; // -1.65339022054652515390e-06 private static final double P5 = 0x1.6376972bea4d0p-25; // 4.13813679705723846039e-08 - private Exp() { - throw new UnsupportedOperationException(); - } - public static double compute(double x) { double y; double hi = 0.0; @@ -1015,8 +1012,8 @@ class FdLibm { } x = hi - lo; } else if (hx < 0x3e300000) { /* when |x|<2**-28 */ - if (huge + x > one) - return one + x; /* trigger inexact */ + if (huge + x > 1.0) + return 1.0 + x; /* trigger inexact */ } else { k = 0; } @@ -1025,9 +1022,9 @@ class FdLibm { t = x * x; c = x - t*(P1 + t*(P2 + t*(P3 + t*(P4 + t*P5)))); if (k == 0) - return one - ((x*c)/(c - 2.0) - x); + return 1.0 - ((x*c)/(c - 2.0) - x); else - y = one - ((lo - (x*c)/(2.0 - c)) - hi); + y = 1.0 - ((lo - (x*c)/(2.0 - c)) - hi); if(k >= -1021) { y = __HI(y, __HI(y) + (k << 20)); /* add k to y's exponent */ @@ -1626,4 +1623,215 @@ class FdLibm { return y; } } + + /** + * Method : + * mathematically sinh(x) if defined to be (exp(x)-exp(-x))/2 + * 1. Replace x by |x| (sinh(-x) = -sinh(x)). + * 2. + * E + E/(E+1) + * 0 <= x <= 22 : sinh(x) := --------------, E=expm1(x) + * 2 + * + * 22 <= x <= lnovft : sinh(x) := exp(x)/2 + * lnovft <= x <= ln2ovft: sinh(x) := exp(x/2)/2 * exp(x/2) + * ln2ovft < x : sinh(x) := x*shuge (overflow) + * + * Special cases: + * sinh(x) is |x| if x is +INF, -INF, or NaN. + * only sinh(0)=0 is exact for finite x. + */ + static final class Sinh { + private Sinh() {throw new UnsupportedOperationException();} + + private static final double shuge = 1.0e307; + + static double compute(double x) { + double t, w, h; + int ix, jx; + /* unsigned */ int lx; + + // High word of |x| + jx = __HI(x); + ix = jx & 0x7fff_ffff; + + // x is INF or NaN + if (ix >= 0x7ff0_0000) { + return x + x; + } + + h = 0.5; + if (jx < 0) { + h = -h; + } + // |x| in [0,22], return sign(x)*0.5*(E+E/(E+1))) + if (ix < 0x4036_0000) { // |x| < 22 + if (ix < 0x3e30_0000) // |x| < 2**-28 + if (shuge + x > 1.0) { // sinh(tiny) = tiny with inexact + return x; + } + t = StrictMath.expm1(Math.abs(x)); + if (ix < 0x3ff0_0000) { + return h*(2.0 * t - t*t/(t + 1.0)); + } + return h*(t + t/(t + 1.0)); + } + + // |x| in [22, log(maxdouble)] return 0.5*exp(|x|) + if (ix < 0x4086_2E42) { + return h*StrictMath.exp(Math.abs(x)); + } + + // |x| in [log(maxdouble), overflowthresold] + lx = __LO(x); + if (ix < 0x4086_33CE || + ((ix == 0x4086_33ce) && + (Long.compareUnsigned(lx, 0x8fb9_f87d) <= 0 ))) { + w = StrictMath.exp(0.5 * Math.abs(x)); + t = h * w; + return t * w; + } + + // |x| > overflowthresold, sinh(x) overflow + return x * shuge; + } + } + + /** + * Method : + * mathematically cosh(x) if defined to be (exp(x)+exp(-x))/2 + * 1. Replace x by |x| (cosh(x) = cosh(-x)). + * 2. + * [ exp(x) - 1 ]^2 + * 0 <= x <= ln2/2 : cosh(x) := 1 + ------------------- + * 2*exp(x) + * + * exp(x) + 1/exp(x) + * ln2/2 <= x <= 22 : cosh(x) := ------------------- + * 2 + * 22 <= x <= lnovft : cosh(x) := exp(x)/2 + * lnovft <= x <= ln2ovft: cosh(x) := exp(x/2)/2 * exp(x/2) + * ln2ovft < x : cosh(x) := huge*huge (overflow) + * + * Special cases: + * cosh(x) is |x| if x is +INF, -INF, or NaN. + * only cosh(0)=1 is exact for finite x. + */ + static final class Cosh { + private Cosh() {throw new UnsupportedOperationException();} + + private static final double huge = 1.0e300; + + static double compute(double x) { + double t, w; + int ix; + /*unsigned*/ int lx; + + // High word of |x| + ix = __HI(x); + ix &= 0x7fff_ffff; + + // x is INF or NaN + if (ix >= 0x7ff0_0000) { + return x*x; + } + + // |x| in [0,0.5*ln2], return 1+expm1(|x|)^2/(2*exp(|x|)) + if (ix < 0x3fd6_2e43) { + t = StrictMath.expm1(Math.abs(x)); + w = 1.0 + t; + if (ix < 0x3c80_0000) { // cosh(tiny) = 1 + return w; + } + return 1.0 + (t * t)/(w + w); + } + + // |x| in [0.5*ln2, 22], return (exp(|x|) + 1/exp(|x|)/2 + if (ix < 0x4036_0000) { + t = StrictMath.exp(Math.abs(x)); + return 0.5*t + 0.5/t; + } + + // |x| in [22, log(maxdouble)] return 0.5*exp(|x|) + if (ix < 0x4086_2E42) { + return 0.5*StrictMath.exp(Math.abs(x)); + } + + // |x| in [log(maxdouble), overflowthresold] + lx = __LO(x); + if (ix<0x4086_33CE || + ((ix == 0x4086_33ce) && + (Integer.compareUnsigned(lx, 0x8fb9_f87d) <= 0))) { + w = StrictMath.exp(0.5*Math.abs(x)); + t = 0.5*w; + return t*w; + } + + // |x| > overflowthresold, cosh(x) overflow + return huge*huge; + } + } + + /** + * Return the Hyperbolic Tangent of x + * + * Method : + * x -x + * e - e + * 0. tanh(x) is defined to be ----------- + * x -x + * e + e + * 1. reduce x to non-negative by tanh(-x) = -tanh(x). + * 2. 0 <= x <= 2**-55 : tanh(x) := x*(one+x) + * -t + * 2**-55 < x <= 1 : tanh(x) := -----; t = expm1(-2x) + * t + 2 + * 2 + * 1 <= x <= 22.0 : tanh(x) := 1- ----- ; t=expm1(2x) + * t + 2 + * 22.0 < x <= INF : tanh(x) := 1. + * + * Special cases: + * tanh(NaN) is NaN; + * only tanh(0)=0 is exact for finite argument. + */ + static final class Tanh { + private Tanh() {throw new UnsupportedOperationException();} + + private static final double tiny = 1.0e-300; + + static double compute(double x) { + double t, z; + int jx, ix; + + // High word of |x|. + jx = __HI(x); + ix = jx & 0x7fff_ffff; + + // x is INF or NaN + if (ix >= 0x7ff0_0000) { + if (jx >= 0) { // tanh(+-inf)=+-1 + return 1.0/x + 1.0; + } else { // tanh(NaN) = NaN + return 1.0/x - 1.0; + } + } + + // |x| < 22 + if (ix < 0x4036_0000) { // |x| < 22 + if (ix<0x3c80_0000) // |x| < 2**-55 + return x*(1.0 + x); // tanh(small) = small + if (ix>=0x3ff0_0000) { // |x| >= 1 + t = StrictMath.expm1(2.0*Math.abs(x)); + z = 1.0 - 2.0/(t + 2.0); + } else { + t = StrictMath.expm1(-2.0*Math.abs(x)); + z= -t/(t + 2.0); + } + } else { // |x| > 22, return +-1 + z = 1.0 - tiny; // raised inexact flag + } + return (jx >= 0)? z: -z; + } + } } diff --git a/src/java.base/share/classes/java/lang/StrictMath.java b/src/java.base/share/classes/java/lang/StrictMath.java index a190c92539f..740438b252d 100644 --- a/src/java.base/share/classes/java/lang/StrictMath.java +++ b/src/java.base/share/classes/java/lang/StrictMath.java @@ -2081,7 +2081,9 @@ public final class StrictMath { * @return The hyperbolic sine of {@code x}. * @since 1.5 */ - public static native double sinh(double x); + public static double sinh(double x) { + return FdLibm.Sinh.compute(x); + } /** * Returns the hyperbolic cosine of a {@code double} value. @@ -2105,7 +2107,9 @@ public final class StrictMath { * @return The hyperbolic cosine of {@code x}. * @since 1.5 */ - public static native double cosh(double x); + public static double cosh(double x) { + return FdLibm.Cosh.compute(x); + } /** * Returns the hyperbolic tangent of a {@code double} value. @@ -2136,7 +2140,9 @@ public final class StrictMath { * @return The hyperbolic tangent of {@code x}. * @since 1.5 */ - public static native double tanh(double x); + public static double tanh(double x) { + return FdLibm.Tanh.compute(x); + } /** * Returns sqrt(x2 +y2) diff --git a/test/jdk/java/lang/StrictMath/ExhaustingTests.java b/test/jdk/java/lang/StrictMath/ExhaustingTests.java index 457e91d2c99..c34a910aaf9 100644 --- a/test/jdk/java/lang/StrictMath/ExhaustingTests.java +++ b/test/jdk/java/lang/StrictMath/ExhaustingTests.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8301833 8302026 + * @bug 8301833 8302026 8301444 * @build Tests * @build FdlibmTranslit * @build ExhaustingTests @@ -73,12 +73,12 @@ public class ExhaustingTests { 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("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("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), @@ -122,7 +122,7 @@ public class ExhaustingTests { */ private static long testBinaryMethods() { long failures = 0; - // Note: pow does _not_ have translit a port + // 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 diff --git a/test/jdk/java/lang/StrictMath/ExpTests.java b/test/jdk/java/lang/StrictMath/ExpTests.java index 9c4b9b20801..45e1816da59 100644 --- a/test/jdk/java/lang/StrictMath/ExpTests.java +++ b/test/jdk/java/lang/StrictMath/ExpTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -140,7 +140,7 @@ public class ExpTests { int failures = 0; double x = start; for (int i = 0; i < count; i++, x += increment) { - failures += testExpCase(x, FdlibmTranslit.Exp.compute(x)); + failures += testExpCase(x, FdlibmTranslit.exp(x)); } return failures; } diff --git a/test/jdk/java/lang/StrictMath/FdlibmTranslit.java b/test/jdk/java/lang/StrictMath/FdlibmTranslit.java index 4dcd98ad4d6..2a9f5d2ca0a 100644 --- a/test/jdk/java/lang/StrictMath/FdlibmTranslit.java +++ b/test/jdk/java/lang/StrictMath/FdlibmTranslit.java @@ -102,10 +102,27 @@ public class FdlibmTranslit { return Log1p.compute(x); } + public static double exp(double x) { + return Exp.compute(x); + } + public static double expm1(double x) { return Expm1.compute(x); } + public static double sinh(double x) { + return Sinh.compute(x); + } + + public static double cosh(double x) { + return Cosh.compute(x); + } + + public static double tanh(double x) { + return Tanh.compute(x); + } + + /** Returns the arcsine of x. * * Method : @@ -620,7 +637,7 @@ public class FdlibmTranslit { * compiler will convert from decimal to binary accurately enough * to produce the hexadecimal values shown. */ - static class Exp { + private static final class Exp { private static final double one = 1.0; private static final double[] halF = {0.5,-0.5,}; private static final double huge = 1.0e+300; @@ -638,7 +655,7 @@ public class FdlibmTranslit { private static final double P4 = -1.65339022054652515390e-06; /* 0xBEBBBD41, 0xC5D26BF1 */ private static final double P5 = 4.13813679705723846039e-08; /* 0x3E663769, 0x72BEA4D0 */ - public static double compute(double x) { + static double compute(double x) { double y,hi=0,lo=0,c,t; int k=0,xsb; /*unsigned*/ int hx; @@ -1216,4 +1233,198 @@ public class FdlibmTranslit { return y; } } + + /** + * Method : + * mathematically sinh(x) if defined to be (exp(x)-exp(-x))/2 + * 1. Replace x by |x| (sinh(-x) = -sinh(x)). + * 2. + * E + E/(E+1) + * 0 <= x <= 22 : sinh(x) := --------------, E=expm1(x) + * 2 + * + * 22 <= x <= lnovft : sinh(x) := exp(x)/2 + * lnovft <= x <= ln2ovft: sinh(x) := exp(x/2)/2 * exp(x/2) + * ln2ovft < x : sinh(x) := x*shuge (overflow) + * + * Special cases: + * sinh(x) is |x| if x is +INF, -INF, or NaN. + * only sinh(0)=0 is exact for finite x. + */ + private static final class Sinh { + private static final double one = 1.0, shuge = 1.0e307; + + static double compute(double x) { + double t,w,h; + int ix,jx; + /* unsigned */ int lx; + + /* High word of |x|. */ + jx = __HI(x); + ix = jx&0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7ff00000) return x+x; + + h = 0.5; + if (jx<0) h = -h; + /* |x| in [0,22], return sign(x)*0.5*(E+E/(E+1))) */ + if (ix < 0x40360000) { /* |x|<22 */ + if (ix<0x3e300000) /* |x|<2**-28 */ + if(shuge+x>one) return x;/* sinh(tiny) = tiny with inexact */ + t = FdlibmTranslit.expm1(Math.abs(x)); + if(ix<0x3ff00000) return h*(2.0*t-t*t/(t+one)); + return h*(t+t/(t+one)); + } + + /* |x| in [22, log(maxdouble)] return 0.5*exp(|x|) */ + if (ix < 0x40862E42) return h*FdlibmTranslit.exp(Math.abs(x)); + + /* |x| in [log(maxdouble), overflowthresold] */ + // Note: the original FDLIBM sources use + // lx = *( (((*(unsigned*)&one)>>29)) + (unsigned*)&x); + // to set lx to the low-order 32 bits of x. The expression + // in question is an alternate way to implement the + // functionality of the C FDLIBM __LO macro and the + // expression is coded to work on both big-edian and + // little-endian machines. However, this port will instead + // use the __LO method call to represent this + // functionality. + lx = __LO(x); + if (ix<0x408633CE || ((ix==0x408633ce)&&(Long.compareUnsigned(lx, 0x8fb9f87d) <= 0 ))) { + w = exp(0.5*Math.abs(x)); + t = h*w; + return t*w; + } + + /* |x| > overflowthresold, sinh(x) overflow */ + return x*shuge; + } + } + + /** + * Method : + * mathematically cosh(x) if defined to be (exp(x)+exp(-x))/2 + * 1. Replace x by |x| (cosh(x) = cosh(-x)). + * 2. + * [ exp(x) - 1 ]^2 + * 0 <= x <= ln2/2 : cosh(x) := 1 + ------------------- + * 2*exp(x) + * + * exp(x) + 1/exp(x) + * ln2/2 <= x <= 22 : cosh(x) := ------------------- + * 2 + * 22 <= x <= lnovft : cosh(x) := exp(x)/2 + * lnovft <= x <= ln2ovft: cosh(x) := exp(x/2)/2 * exp(x/2) + * ln2ovft < x : cosh(x) := huge*huge (overflow) + * + * Special cases: + * cosh(x) is |x| if x is +INF, -INF, or NaN. + * only cosh(0)=1 is exact for finite x. + */ + private static final class Cosh { + private static final double one = 1.0, half=0.5, huge = 1.0e300; + static double compute(double x) { + double t,w; + int ix; + /*unsigned*/ int lx; + + /* High word of |x|. */ + ix = __HI(x); + ix &= 0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7ff00000) return x*x; + + /* |x| in [0,0.5*ln2], return 1+expm1(|x|)^2/(2*exp(|x|)) */ + if(ix<0x3fd62e43) { + t = expm1(Math.abs(x)); + w = one+t; + if (ix<0x3c800000) return w; /* cosh(tiny) = 1 */ + return one+(t*t)/(w+w); + } + + /* |x| in [0.5*ln2,22], return (exp(|x|)+1/exp(|x|)/2; */ + if (ix < 0x40360000) { + t = exp(Math.abs(x)); + return half*t+half/t; + } + + /* |x| in [22, log(maxdouble)] return half*exp(|x|) */ + if (ix < 0x40862E42) return half*exp(Math.abs(x)); + + /* |x| in [log(maxdouble), overflowthresold] */ + // See note above in the sinh implementation for how this + // transliteration port uses __LO(x) in the line below + // that differs from the idiom used in the original FDLIBM. + lx = __LO(x); + if (ix<0x408633CE || + ((ix==0x408633ce)&&(Integer.compareUnsigned(lx, 0x8fb9f87d) <= 0))) { + w = exp(half*Math.abs(x)); + t = half*w; + return t*w; + } + + /* |x| > overflowthresold, cosh(x) overflow */ + return huge*huge; + } + } + + /** + * Return the Hyperbolic Tangent of x + * + * Method : + * x -x + * e - e + * 0. tanh(x) is defined to be ----------- + * x -x + * e + e + * 1. reduce x to non-negative by tanh(-x) = -tanh(x). + * 2. 0 <= x <= 2**-55 : tanh(x) := x*(one+x) + * -t + * 2**-55 < x <= 1 : tanh(x) := -----; t = expm1(-2x) + * t + 2 + * 2 + * 1 <= x <= 22.0 : tanh(x) := 1- ----- ; t=expm1(2x) + * t + 2 + * 22.0 < x <= INF : tanh(x) := 1. + * + * Special cases: + * tanh(NaN) is NaN; + * only tanh(0)=0 is exact for finite argument. + */ + private static final class Tanh { + private static final double one=1.0, two=2.0, tiny = 1.0e-300; + static double compute(double x) { + double t,z; + int jx,ix; + + /* High word of |x|. */ + jx = __HI(x); + ix = jx&0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7ff00000) { + if (jx>=0) return one/x+one; /* tanh(+-inf)=+-1 */ + else return one/x-one; /* tanh(NaN) = NaN */ + } + + /* |x| < 22 */ + if (ix < 0x40360000) { /* |x|<22 */ + if (ix<0x3c800000) /* |x|<2**-55 */ + return x*(one+x); /* tanh(small) = small */ + if (ix>=0x3ff00000) { /* |x|>=1 */ + t = expm1(two*Math.abs(x)); + z = one - two/(t+two); + } else { + t = expm1(-two*Math.abs(x)); + z= -t/(t+two); + } + /* |x| > 22, return +-1 */ + } else { + z = one - tiny; /* raised inexact flag */ + } + return (jx>=0)? z: -z; + } + } } diff --git a/test/jdk/java/lang/StrictMath/HyperbolicTests.java b/test/jdk/java/lang/StrictMath/HyperbolicTests.java index 4dfc7596d38..0314abace7b 100644 --- a/test/jdk/java/lang/StrictMath/HyperbolicTests.java +++ b/test/jdk/java/lang/StrictMath/HyperbolicTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -20,17 +20,27 @@ * 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 4851625 + * @bug 4851625 8301444 + * @key randomness + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @build Tests + * @build FdlibmTranslit + * @build HyperbolicTests + * @run main HyperbolicTests * @summary Tests for StrictMath.{sinh, cosh, tanh} */ /** * The tests in ../Math/HyperbolicTests.java test properties that * should hold for any implementation of the hyperbolic functions - * sinh, cos, and tanh, including the FDLIBM-based ones required by + * sinh, cosh, and tanh, including the FDLIBM-based ones required by * the StrictMath class. Therefore, the test cases in * ../Math/HyperbolicTests.java are run against both the Math and * StrictMath versions of the hyperbolic methods. The role of this @@ -42,22 +52,208 @@ public class HyperbolicTests { private HyperbolicTests(){} - static int testSinhCase(double input, double expected) { + public static void main(String... args) { + int failures = 0; + + failures += testAgainstTranslitCommon(); + + failures += testAgainstTranslitSinh(); + failures += testAgainstTranslitCosh(); + failures += testAgainstTranslitTanh(); + + failures += testSinh(); + failures += testCosh(); + failures += testTanh(); + + if (failures > 0) { + System.err.println("Testing the hyperbolics incurred " + + failures + " failures."); + throw new RuntimeException(); + } + } + + /** + * Bundle together groups of testing methods. + */ + private static enum HyperbolicTest { + SINH(HyperbolicTests::testSinhCase, FdlibmTranslit::sinh), + COSH(HyperbolicTests::testCoshCase, FdlibmTranslit::cosh), + TANH(HyperbolicTests::testTanhCase, FdlibmTranslit::tanh); + + private DoubleDoubleToInt testCase; + private DoubleUnaryOperator transliteration; + + HyperbolicTest(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 : HyperbolicTest.values()) { + for (double testPoint : pointsOfInterest) { + failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, testMethods); + } + } + + return failures; + } + + /** + * Test StrictMath.sinh against transliteration port of sinh. + */ + private static int testAgainstTranslitSinh() { + int failures = 0; + double x; + + // Probe near decision points in the FDLIBM algorithm. + double[] decisionPoints = { + 0.0, + + 22.0, + -22.0, + + 0x1.0p-28, + -0x1.0p-28, + + // StrictMath.log(Double.MAX_VALUE) ~= 709.782712893384 + 0x1.62e42fefa39efp9, + -0x1.62e42fefa39efp9, + + // Largest argument with finite sinh, 710.4758600739439 + 0x1.633ce8fb9f87dp9, + -0x1.633ce8fb9f87dp9, + }; + + for (double testPoint : decisionPoints) { + failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, HyperbolicTest.SINH); + } + + return failures; + } + + /** + * Test StrictMath.cosh against transliteration port of cosh. + */ + private static int testAgainstTranslitCosh() { + int failures = 0; + double x; + + // Probe near decision points in the FDLIBM algorithm. + double[] decisionPoints = { + 0.0, + + 22.0, + -22.0, + + // StrictMath.log(2)/2 ~= 0.34657359027997264 + 0x1.62e42fefa39efp-2, + -0x1.62e42fefa39efp-2, + + 0x1.0p-28, + -0x1.0p-28, + + // StrictMath.log(Double.MAX_VALUE) ~= 709.782712893384 + 0x1.62e42fefa39efp9, + -0x1.62e42fefa39efp9, + + // Largest argument with finite cosh, 710.4758600739439 + 0x1.633ce8fb9f87dp9, + -0x1.633ce8fb9f87dp9, + }; + + for (double testPoint : decisionPoints) { + failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, HyperbolicTest.COSH); + } + + return failures; + } + + /** + * Test StrictMath.tanh against transliteration port of tanh + */ + private static int testAgainstTranslitTanh() { + int failures = 0; + double x; + + // Probe near decision points in the FDLIBM algorithm. + double[] decisionPoints = { + 0.0, + + 0x1.0p-55, + -0x1.0p-55, + + 1.0, + -1.0, + + 22.0, + }; + + for (double testPoint : decisionPoints) { + failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, HyperbolicTest.COSH); + } + + return failures; + } + + private interface DoubleDoubleToInt { + int apply(double x, double y); + } + + private static int testRange(double start, double increment, int count, + HyperbolicTest 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, + HyperbolicTest 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 testSinhCase(double input, double expected) { return Tests.test("StrictMath.sinh(double)", input, StrictMath::sinh, expected); } - static int testCoshCase(double input, double expected) { + private static int testCoshCase(double input, double expected) { return Tests.test("StrictMath.cosh(double)", input, StrictMath::cosh, expected); } - static int testTanhCase(double input, double expected) { + private static int testTanhCase(double input, double expected) { return Tests.test("StrictMath.tanh(double)", input, StrictMath::tanh, expected); } - static int testSinh() { + private static int testSinh() { int failures = 0; double [][] testCases = { {0x1.5798ee2308c3ap-27, 0x1.5798ee2308c3bp-27}, @@ -147,12 +343,12 @@ public class HyperbolicTests { }; for (double[] testCase: testCases) - failures+=testSinhCase(testCase[0], testCase[1]); + failures += testSinhCase(testCase[0], testCase[1]); return failures; } - static int testCosh() { + private static int testCosh() { int failures = 0; double [][] testCases = { {0x1.fffffffffb49fp-8, 0x1.00020000aaaabp0}, @@ -188,12 +384,12 @@ public class HyperbolicTests { }; for (double[] testCase: testCases) - failures+=testCoshCase(testCase[0], testCase[1]); + failures += testCoshCase(testCase[0], testCase[1]); return failures; } - static int testTanh() { + private static int testTanh() { int failures = 0; double [][] testCases = { {0x1.5798ee2308c36p-27, 0x1.5798ee2308c36p-27}, @@ -257,23 +453,8 @@ public class HyperbolicTests { }; for (double[] testCase: testCases) - failures+=testTanhCase(testCase[0], testCase[1]); + failures += testTanhCase(testCase[0], testCase[1]); return failures; } - - - public static void main(String [] argv) { - int failures = 0; - - failures += testSinh(); - failures += testCosh(); - failures += testTanh(); - - if (failures > 0) { - System.err.println("Testing the hyperbolics incurred " - + failures + " failures."); - throw new RuntimeException(); - } - } }