From fa2f4d82f5337afab2291e4757387e4bc328265d Mon Sep 17 00:00:00 2001 From: Anton Artemov Date: Fri, 20 Feb 2026 08:31:18 +0000 Subject: [PATCH] 8377223: Port fdlibm atanh to Java Reviewed-by: darcy, rgiulietti --- .../share/classes/java/lang/FdLibm.java | 48 +++ .../share/classes/java/lang/Math.java | 34 +- .../share/classes/java/lang/StrictMath.java | 36 +- test/jdk/java/lang/Math/HyperbolicTests.java | 386 +++++++++++++++++- .../java/lang/StrictMath/ExhaustingTests.java | 1 + .../java/lang/StrictMath/FdlibmTranslit.java | 50 +++ .../java/lang/StrictMath/HyperbolicTests.java | 102 ++++- 7 files changed, 651 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/java/lang/FdLibm.java b/src/java.base/share/classes/java/lang/FdLibm.java index 8e75f8f6994..5a6bdc9b94e 100644 --- a/src/java.base/share/classes/java/lang/FdLibm.java +++ b/src/java.base/share/classes/java/lang/FdLibm.java @@ -3607,4 +3607,52 @@ final class FdLibm { } } } + + /** + * Return the Inverse Hyperbolic Tangent of x + * Method : + * + * + * atanh(x) is defined so that atanh(tanh(alpha)) = alpha, -∞ < alpha < ∞ + * and tanh(atanh(x)) = x, -1 < x < 1; + * It can be written as atanh(x) = 0.5 * log1p(2 * x/(1-x)), -1 < x < 1; + * 1. + * atanh(x) := 0.5 * log1p(2 * x/(1 - x)), if |x| >= 0.5, + * := 0.5 * log1p(2x + 2x * x/(1 - x)), if |x| < 0.5. + * + * + * + * Special cases: + * only atanh(±0)=±0 is exact for finite x. + * atanh(NaN) is NaN + * atanh(±1) is ±∞ + */ + static final class Atanh { + + static double compute(double x) { + double t; + int hx,ix; + int lx; // unsigned + hx = __HI(x); // high word + lx = __LO(x); // low word + ix = hx & 0x7fff_ffff; + if ((ix | ((lx | (-lx)) >>> 31)) > 0x3ff0_0000) { // |x| > 1 + return (x - x) / (x - x); + } + if (ix == 0x3ff0_0000) { + return x / 0.0; + } + if (ix < 0x3e30_0000 && (HUGE + x) > 0.0) { + return x; // x<2**-28 + } + x = __HI(x, ix); // x <- |x| + if (ix < 0x3fe0_0000) { // x < 0.5 + t = x + x; + t = 0.5 * Log1p.compute(t + t * x / (1.0 - x)); + } else { + t = 0.5 * Log1p.compute((x + x) / (1.0 - x)); + } + return hx >= 0 ? t : -t; + } + } } diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java index 4f729fe82cb..38a5328fa0b 100644 --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -109,7 +109,7 @@ import static java.lang.Double.*; * acos acos}, {@link atan atan}, {@link exp exp}, {@link expm1 * expm1}, {@link log log}, {@link log10 log10}, {@link log1p log1p}, * {@link sinh sinh}, {@link cosh cosh}, {@link tanh tanh}, {@link asinh asinh}, - * {@link acosh acosh}, {@link hypot hypot}, and {@link pow pow}. + * {@link acosh acosh}, {@link atanh atanh}, {@link hypot hypot}, and {@link pow pow}. * (The {@link sqrt sqrt} operation is a required part of IEEE 754 * from a different section of the standard.) The special case behavior * of the recommended operations generally follows the guidance of the IEEE 754 @@ -2814,6 +2814,38 @@ public final class Math { return StrictMath.acosh(x); } + /** + * Returns the inverse hyperbolic tangent of a {@code double} value. + * The inverse hyperbolic tangent of x is defined to be the function such that + * atanh({@linkplain Math#tanh tanh(x)}) = x for any x. + * Note that the domain of the exact atanh is (-1; 1), the range is unrestricted. + * + *

Special cases: + *

+ *

The computed result must be within 2.5 ulps of the exact result. + * @param x The number whose inverse hyperbolic tangent is to be returned. + * @return The inverse hyperbolic tangent of {@code x}. + * @since 27 + */ + public static double atanh(double x) { + return StrictMath.atanh(x); + } + /** * Returns sqrt(x2 +y2) * without intermediate overflow or underflow. diff --git a/src/java.base/share/classes/java/lang/StrictMath.java b/src/java.base/share/classes/java/lang/StrictMath.java index 9421b41620b..a8f67ef58ba 100644 --- a/src/java.base/share/classes/java/lang/StrictMath.java +++ b/src/java.base/share/classes/java/lang/StrictMath.java @@ -76,8 +76,8 @@ import jdk.internal.vm.annotation.IntrinsicCandidate; * {@code exp}, {@code log}, {@code log10}, * {@code cbrt}, {@code atan2}, {@code pow}, * {@code sinh}, {@code cosh}, {@code tanh}, - * {@code asinh}, {@code acosh}, {@code hypot}, - * {@code expm1}, and {@code log1p}. + * {@code asinh}, {@code acosh},{@code atanh}, + * {@code hypot}, {@code expm1}, and {@code log1p}. * *

* The platform uses signed two's complement integer arithmetic with @@ -2222,6 +2222,38 @@ public final class StrictMath { return FdLibm.Acosh.compute(x); } + /** + * Returns the inverse hyperbolic tangent of a {@code double} value. + * The inverse hyperbolic tangent of x is defined to be the function such that + * atanh({@linkplain Math#tanh tanh(x)}) = x for any x. + * Note that the domain of the exact atanh is (-1; 1), the range is unrestricted. + * + *

Special cases: + *

+ * + * @param x The number whose inverse hyperbolic tangent is to be returned. + * @return The inverse hyperbolic tangent of {@code x}. + * @since 27 + */ + public static double atanh(double x) { + return FdLibm.Atanh.compute(x); + } + /** * Returns sqrt(x2 +y2) * without intermediate overflow or underflow. diff --git a/test/jdk/java/lang/Math/HyperbolicTests.java b/test/jdk/java/lang/Math/HyperbolicTests.java index ef37a102847..ac5526c1953 100644 --- a/test/jdk/java/lang/Math/HyperbolicTests.java +++ b/test/jdk/java/lang/Math/HyperbolicTests.java @@ -27,7 +27,7 @@ * @build Tests * @build HyperbolicTests * @run main HyperbolicTests - * @summary Tests for {Math, StrictMath}.{sinh, cosh, tanh, asinh, acosh} + * @summary Tests for {Math, StrictMath}.{sinh, cosh, tanh, asinh, acosh, atanh} */ import static java.lang.Double.longBitsToDouble; @@ -45,6 +45,7 @@ public class HyperbolicTests { failures += testTanh(); failures += testAsinh(); failures += testAcosh(); + failures += testAtanh(); if (failures > 0) { System.err.println("Testing the hyperbolic functions incurred " @@ -2101,4 +2102,387 @@ public class HyperbolicTests { failures += Tests.testUlpDiffWithAbsBound("StrictMath.acosh", input, StrictMath::acosh, expected, ulps, Double.POSITIVE_INFINITY); return failures; } + + /** + * Test accuracy of {Math, StrictMath}.atanh. The specified + * accuracy is 2.5 ulps. + * + * The defintion of atanh(x) is + * + * atanh(tanh(x)) = x + * + * Can be also written as + * + * 0.5 * log1p(2 * x / (1-x0)) + * + * Taylor expansion: + * + * x + x^3 / 3 + x^5 / 5 + ... + * + * Therefore, + * + * 1. For small values of x, tanh(x) ~= x. + * + * Additionally, atanh is an odd function; atanh(-x) = -atanh(x). + * + */ + static int testAtanh() { + int failures = 0; + /* + * Array elements below generated using a quad atanh + * implementation. Rounded to a double, the quad result + * *should* be correctly rounded, unless we are quite unlucky. + * Assuming the quad value is a correctly rounded double, the + * allowed error is 3.0 ulps instead of 2.5 since the quad + * value rounded to double can have its own 1/2 ulp error. + */ + double [][] testCases = { + // x atanh(x) + {+0.00000000000000000000000000000000000e+00 , +0.00000000000000000000000000000000000e+00 }, + {+2.00000000000000004163336342344337027e-02 , +2.00026673068495811335374182828113576e-02 }, + {+4.00000000000000008326672684688674053e-02 , +4.00213538367682137458906520059156020e-02 }, + {+5.99999999999999977795539507496869192e-02 , +6.00721559210316214332906986486015264e-02 }, + {+8.00000000000000016653345369377348106e-02 , +8.01713250375896933655527689248809233e-02 }, + {+1.00000000000000005551115123125782702e-01 , +1.00335347731075586242913545116385118e-01 }, + {+1.20000000000000009436895709313830594e-01 , +1.20581028408444044805093075807633198e-01 }, + {+1.40000000000000013322676295501878485e-01 , +1.40925576070493877554736186975308236e-01 }, + {+1.60000000000000003330669073875469621e-01 , +1.61386696131525518759511362898215609e-01 }, + {+1.79999999999999993338661852249060757e-01 , +1.81982688600705816490251036770517457e-01 }, + {+1.99999999999999983346654630622651894e-01 , +2.02732554054082173641771797964103718e-01 }, + {+2.19999999999999973354647408996243030e-01 , +2.23656109021832382650747515743625830e-01 }, + {+2.39999999999999963362640187369834166e-01 , +2.44774112659352854083413297101694196e-01 }, + {+2.59999999999999953370632965743425302e-01 , +2.66108406873654071753827618842061943e-01 }, + {+2.79999999999999971134201359745929949e-01 , +2.87682072451780896117822911968144088e-01 }, + {+2.99999999999999988897769753748434596e-01 , +3.09519604203111703273814331202206426e-01 }, + {+3.20000000000000006661338147750939243e-01 , +3.31647108705132085025104001081840030e-01 }, + {+3.40000000000000024424906541753443889e-01 , +3.54092528962242939723929765493171945e-01 }, + {+3.60000000000000042188474935755948536e-01 , +3.76885901188190124469133896689497444e-01 }, + {+3.80000000000000059952043329758453183e-01 , +4.00059650056056638206593741845596981e-01 }, + {+4.00000000000000077715611723760957830e-01 , +4.23648930193601899373639138690042112e-01 }, + {+4.20000000000000095479180117763462476e-01 , +4.47692023527420813002158901561072580e-01 }, + {+4.40000000000000113242748511765967123e-01 , +4.72230804420425833981180600242988051e-01 }, + {+4.60000000000000131006316905768471770e-01 , +4.97311287572031193620508878977899997e-01 }, + {+4.80000000000000148769885299770976417e-01 , +5.22984277591344047465367134186014130e-01 }, + {+5.00000000000000111022302462515654042e-01 , +5.49306144334054993727359235148812474e-01 }, + {+5.20000000000000128785870856518158689e-01 , +5.76339754969192906113997153691857594e-01 }, + {+5.40000000000000146549439250520663336e-01 , +6.04155602962267286054127265014583953e-01 }, + {+5.60000000000000164313007644523167983e-01 , +6.32833186665638181077146946128475799e-01 }, + {+5.80000000000000182076576038525672629e-01 , +6.62462707371799523213172030295196960e-01 }, + {+6.00000000000000199840144432528177276e-01 , +6.93147180559945621667457797283512021e-01 }, + {+6.20000000000000217603712826530681923e-01 , +7.25005087752999506268291290927001216e-01 }, + {+6.40000000000000235367281220533186570e-01 , +7.58173744684044609195455590065769225e-01 }, + {+6.60000000000000253130849614535691217e-01 , +7.92813631870191370108450047457113058e-01 }, + {+6.80000000000000270894418008538195863e-01 , +8.29114038301766692728933700083100051e-01 }, + {+7.00000000000000288657986402540700510e-01 , +8.67300527694053760423196460163172710e-01 }, + {+7.20000000000000306421554796543205157e-01 , +9.07644983319125195441126107009775370e-01 }, + {+7.40000000000000324185123190545709804e-01 , +9.50479380596524207852088721432242171e-01 }, + {+7.60000000000000341948691584548214450e-01 , +9.96215082345103890579587353760865510e-01 }, + {+7.80000000000000359712259978550719097e-01 , +1.04537054846688556569856922586455819e+00 }, + {+8.00000000000000377475828372553223744e-01 , +1.09861228866811073993921293846013796e+00 }, + {+8.20000000000000395239396766555728391e-01 , +1.15681746459031653565363995495937725e+00 }, + {+8.40000000000000413002965160558233038e-01 , +1.22117351768460359355809310163905228e+00 }, + {+8.60000000000000430766533554560737684e-01 , +1.29334467204897297035888921761818841e+00 }, + {+8.80000000000000448530101948563242331e-01 , +1.37576765652097643587819823310934128e+00 }, + {+9.00000000000000466293670342565746978e-01 , +1.47221948958322268418172604524275263e+00 }, + {+9.20000000000000484057238736568251625e-01 , +1.58902691517397596123778549185721945e+00 }, + {+9.40000000000000501820807130570756271e-01 , +1.73804934491764087653605718356588900e+00 }, + {+9.60000000000000519584375524573260918e-01 , +1.94591014905531993245708137324632564e+00 }, + {+9.80000000000000537347943918575765565e-01 , +2.29755992506730853281874022244325249e+00 }, + }; + + for(double [] testCase : testCases) { + failures += testAtanhCaseWithUlpDiff(testCase[0], + testCase[1], + 3.0); + } + + for(double nan : Tests.NaNs) { + failures += testAtanhCaseWithUlpDiff(nan, NaNd, 0); + } + + double [][] specialTestCases = { + {0.0, 0.0}, + {-0.0, -0.0}, + {1.0, Double.POSITIVE_INFINITY}, + {-1.0, Double.NEGATIVE_INFINITY}, + {2.0, NaNd}, + {-2.0, NaNd}, + }; + + for(double [] specialTestCase : specialTestCases) { + failures += testAtanhCaseWithUlpDiff(specialTestCase[0], + specialTestCase[1], + 0.0); + } + + // For powers of 2 less than 2^(-27), the second and + // subsequent terms of the Taylor series expansion will get + // rounded away since |n-n^3| > 53, the binary precision of a + // double significand. + + for(int i = DoubleConsts.MIN_SUB_EXPONENT; i < -27; i++) { + double d = Math.scalb(2.0, i); + + // Result and expected are the same. + failures += testAtanhCaseWithUlpDiff(d, d, 2.5); + } + + failures += testAtanhAdditionalTests(); + + return failures; + } + + /** + * Test accuracy of {Math, StrictMath}.tanh using quad precision + * tanh implementation as the reference. There are additional tests. + * The specified accuracy is 2.5 ulps. + * + */ + static int testAtanhAdditionalTests() { + int failures = 0; + /* + * Array elements below are generated using a quad precision tanh + * implementation (libquadmath). Rounded to a double, the quad result + * *should* be correctly rounded, unless we are quite unlucky. + * Assuming the quad value is a correctly rounded double, the + * allowed error is 3.0 ulps instead of 2.5 since the quad + * value rounded to double can have its own 1/2 ulp error. + */ + double[][] testCases = { + // x atanh(x) + {+9.39017107929201566562937841808889061e-01 , +1.72967155564501022599234830162276798e+00 }, + {+4.56590977869321346105380143853835762e-01 , +4.92995868916526654745557405930485720e-01 }, + {-6.57855028101722583144805867050308734e-01 , -7.89022676186288902931611202476188124e-01 }, + {+4.62405425240985490376033339998684824e-01 , +5.00366606474614321339885208418466006e-01 }, + {-9.83365261789753697385663144814316183e-01 , -2.39052856803141716223262787771779768e+00 }, + {-6.27582201138608741786129030515439808e-01 , -7.37417270574716377975381198846020522e-01 }, + {-9.83196938676588638728048863413278013e-01 , -2.38545217282555498582477166161511345e+00 }, + {+7.36681396907350904967870519612915814e-01 , +9.43183305171108845024940622694003154e-01 }, + {-7.68733978209885648880117514636367559e-01 , -1.01722532284268312448347897782273734e+00 }, + {-8.99650279001481090190850409271661192e-01 , -1.47038189478731874369381319202006168e+00 }, + {+6.23383381659472490810003364458680153e-02 , +6.24192773197774301522605242534216118e-02 }, + {-8.14139384718910008587045012973248959e-01 , -1.13918472673420532816505691112579313e+00 }, + {-4.89518635342238050967011986358556896e-01 , -5.35427075187260852558782518094419668e-01 }, + {-6.08295892536873550326959048106800765e-01 , -7.06211859359034873213744847874549993e-01 }, + {+9.36079053287340978606323460553539917e-01 , +1.70538649403259128938060088254525967e+00 }, + {+8.31593360663532843446432707423809916e-01 , +1.19328001443669418326658153597705593e+00 }, + {+6.56422583223821076714443734090309590e-01 , +7.86501511439435312578862353672697253e-01 }, + {-3.45789522181036979020518629113212228e-01 , -3.60653496973375779632391162421477716e-01 }, + {+5.32079052777165939502879155043046921e-01 , +5.93040792269778100045583612458499490e-01 }, + {-3.36182931512335247958844774984754622e-01 , -3.49782828626977949006925091676664461e-01 }, + {+6.13630231412017335124176042882027104e-01 , +7.14723430606319696079463920489037224e-01 }, + {+7.39217182016670415478643008100334555e-01 , +9.48751224833067617682882507817093541e-01 }, + {-7.64409441646018983362864673836156726e-03 , -7.64424330871250337925427639013540391e-03 }, + {-9.74816033382744917545892349153291434e-01 , -2.18101148933100542106171185218910695e+00 }, + {+5.52687515297161868765840608830330893e-01 , +6.22242584006598996817110126253907160e-01 }, + {+9.93416759200363430615254856093088165e-01 , +2.85653913150680081054657418761203993e+00 }, + {+9.07659865436335255850508474395610392e-01 , +1.51407684709244159021847264731507603e+00 }, + {+6.60272764758356833780794659105595201e-01 , +7.93297068812459385386864151954890303e-01 }, + {+9.63130814953695568725322573300218210e-01 , +1.98745986190671136058731587311090272e+00 }, + {-5.70877067883640121337407435930799693e-01 , -6.48822975697686437517619163352737241e-01 }, + {-8.11543971448019485492864077968988568e-01 , -1.13153503707350652581786706338031953e+00 }, + {+6.25363929368137561048968109389534220e-01 , +7.33765974893577300297563917687072047e-01 }, + {-2.98846490310910706256208868580870330e-01 , -3.08252492314447063039336121789383998e-01 }, + {-5.97049745291272859759601487894542515e-01 , -6.88550090030688204308547683807415861e-01 }, + {+8.32383827085564576009346637874841690e-01 , +1.19584817769769645815260824209818595e+00 }, + {-6.63715390745103883674005373904947191e-01 , -7.99425357382537999237287552090479860e-01 }, + {-3.94238412786191538828006741823628545e-01 , -4.16808558150307726736043589182851417e-01 }, + {-9.76759747274416945117536670295521617e-01 , -2.22166424268955747060822814557733573e+00 }, + {+8.57442746954987455865193624049425125e-01 , +1.28360612935435243492898897724236702e+00 }, + {-4.24565955117479187919116156990639865e-01 , -4.53248918353154620159372526887584647e-01 }, + {+3.42862260746481295470289296645205468e-01 , +3.57332492545316699435359810051341636e-01 }, + {+9.34459049867542246303742103918921202e-01 , +1.69245393271799745831802454769856267e+00 }, + {+7.64520478549035598092586951679550111e-01 , +1.00700514849153029184678063244721996e+00 }, + {-9.63864704576277597780631367641035467e-01 , -1.99769974929677935746994673118167405e+00 }, + {-1.63366842704400605512660149543080479e-01 , -1.64843919557971079920329622870569601e-01 }, + {+9.94878693411232140064726081618573517e-01 , +2.98246446127683966842287869896800818e+00 }, + {+7.97196021651951247655176757689332590e-01 , +1.09087153746565597453258159715989760e+00 }, + {+8.42371664350162419054868223611265421e-01 , +1.22928450632196951329839160548759750e+00 }, + {+6.96520087724423975217291626904625446e-01 , +8.60509501716176099565750762116593117e-01 }, + {+2.01836732473232416396058397367596626e-01 , +2.04646551779576522368238234135183581e-01 }, + {+5.05939365449320055390103334502782673e-01 , +5.57256946951148119219566387597645954e-01 }, + {-6.01430574320412913991162895399611443e-01 , -6.95385458565007214254554832195678104e-01 }, + {-4.47649318961238940062230540206655860e-01 , -4.81756612137591759595253632500957881e-01 }, + {+3.37215361458020113083478008775273338e-01 , +3.50947266793232870614512911957588554e-01 }, + {+5.14125575037364890285118690371746197e-01 , +5.68321583728012649428480801619053327e-01 }, + {-2.15214858343344861424384362180717289e-01 , -2.18633101430445893274568498462426012e-01 }, + {-9.20630621904034773805847180483397096e-01 , -1.59314811621779716899638118653946385e+00 }, + {+9.19047868224296293782060729427030310e-01 , +1.58286321290197920098639225270320808e+00 }, + {-2.90913484009795997309311132994480431e-01 , -2.99563914714840125917110413508169726e-01 }, + {+3.17993101000002553746526245959103107e-03 , +3.17994172851141737533171613593260162e-03 }, + {-7.16021961571153608971940229821484536e-01 , -8.99433586194582637259073931486986768e-01 }, + {+9.92926371376932737078391255636233836e-01 , +2.82049289268529672262432232518141968e+00 }, + {-7.42199723778889541847547661745920777e-01 , -9.55359320438959545982214260560204172e-01 }, + {+4.01228574786033020949105321051320061e-01 , +4.25112376436892578257996500834612471e-01 }, + {-4.41626489426403034954660142830107361e-01 , -4.74249574732580717257665801841627296e-01 }, + {-7.21334187852223251979921769816428423e-01 , -9.10420850885118135075313207007968241e-01 }, + {+2.82138790772897052328005429444601759e-01 , +2.90004322151609047453793285881602424e-01 }, + {+9.42986643599090679224161704041762277e-01 , +1.76434800601646585550841462860153977e+00 }, + {-5.29497382099672897481923428131267428e-01 , -5.89446464446382266025999977702386855e-01 }, + {+8.90579204675862490248050562513526529e-01 , +1.42471878072594370872155352848645131e+00 }, + {+7.91047774827229877026013582508312538e-01 , +1.07422521925289214702426279427778303e+00 }, + {+4.75836513423619322793456376530230045e-01 , +5.17588301409701923326927500834908347e-01 }, + {+9.32576831661000027473562568047782406e-01 , +1.67781041728427657120484424620161805e+00 }, + {+4.49528137196881449888508086587535217e-01 , +4.84108758524071045231641283604748208e-01 }, + {-6.26946229789648867836149292998015881e-01 , -7.36368746336877301544734566556181860e-01 }, + {+9.58777722962137812778848910966189578e-01 , +1.93054856596755898497650787204101231e+00 }, + {-9.52075725696122754371231167169753462e-01 , -1.85351320681716370080636444991087747e+00 }, + {+9.66628065084665144546249848644947633e-01 , +2.03818021917486263862909093415522483e+00 }, + {-9.90577960901121823411585864960215986e-01 , -2.67656438907628996596144534201915287e+00 }, + {-4.29129153811968588883019037893973291e-01 , -4.58828779570547333956754968138122237e-01 }, + {+9.21075240738198020018501210870454088e-01 , +1.59607267263080478465215862819487820e+00 }, + {-3.36155477527195101217216688382904977e-01 , -3.49751876782073295503516199927519371e-01 }, + {-4.48919523178084922676589485490694642e-01 , -4.83346273396370947315596684554195125e-01 }, + {-7.74714213440744980943009068141691387e-01 , -1.03201246185914269057125935805127686e+00 }, + {-9.33695607057150489538344118045642972e-01 , -1.68646605985566878841417421790327581e+00 }, + {-9.70495709947644780157816057908348739e-01 , -2.10075237060881671900628221642575807e+00 }, + {-9.36541591900329706277261720970273018e-01 , -1.70913713792726775520494333833202263e+00 }, + {+7.37812568478896091406227242259774357e-02 , +7.39155762025176670781318988781666040e-02 }, + {-5.04698425671640915624038825626485050e-01 , -5.55590478683141718256493322563558866e-01 }, + {+8.86920594265426132096763467416167259e-01 , +1.40730559326330763357753153399607213e+00 }, + {-5.34965721339583311078058613929897547e-01 , -5.97076103988724933820435448973192479e-01 }, + {-5.04547440425485049786402669269591570e-01 , -5.55387910636784813509136387661093775e-01 }, + {+6.29048733546408000982808061962714419e-01 , +7.39840413838973059800583747029269377e-01 }, + {-9.78682200705922600292296920088119805e-01 , -2.26532198777189725437096465346095003e+00 }, + {+2.38919916302204882185833412222564220e-03 , +2.38920370910435739436106539813089152e-03 }, + {+9.39119411473845255855508185049984604e-01 , +1.73053742845170764254605902984133442e+00 }, + {-6.12092729070447649775132958893664181e-01 , -7.12261063519474464072359286081706738e-01 }, + {-4.28286500855559193468025114270858467e-01 , -4.57796381674494452786278030205231882e-01 }, + {-3.27343597141078301859806742868386209e-01 , -3.39850137417994572135528032299471827e-01 }, + {+8.67784128381645536443045330088352785e-01 , +1.32403603565363914121499259355873480e+00 }, + {+7.52660015475228982850808279181364924e-01 , +9.79063037984556159433073554464697266e-01 }, + {+7.94013223397652012636172003112733364e-01 , +1.08219921900739052536746217766952318e+00 }, + {+7.09905313419819106179886603058548644e-01 , +8.86992950278189161084285873777251874e-01 }, + {-1.98448575704946783559989853529259562e-01 , -2.01117007840272246446085014680650093e-01 }, + {-5.42433878231522781376838793221395463e-01 , -6.07597742754044712584754611484481699e-01 }, + {-7.54416871023775081894768845813814551e-01 , -9.83128156322049799142775913826928208e-01 }, + {+3.79392548957375952412007791281212121e-01 , +3.99349870596906125894068556877626311e-01 }, + {-7.19279634651493138264299886941444129e-01 , -9.06150816136239309657351447814954486e-01 }, + {-7.23976590297829680764607473975047469e-01 , -9.15951596268038628402616624621418121e-01 }, + {+9.31431733675747830503155455517116934e-01 , +1.66909350005993386837360519102696526e+00 }, + {+5.37270796945802042721140878711594269e-01 , +6.00310952521635654514167563653463826e-01 }, + {+5.05516649275373541883027428411878645e-01 , +5.56688962620397076749424137775916558e-01 }, + {+6.47849417517198489235852321144193411e-01 , +7.71583729081504579676124881689567952e-01 }, + {-5.41124640015404612114480187301523983e-01 , -6.05744544146300297202878069447139070e-01 }, + {+8.08308086737189235293499223189428449e-01 , +1.12212873178291911884370700806026369e+00 }, + {-3.98280706273062445887944704736582935e-01 , -4.21603823445725768059979840445297322e-01 }, + {+8.21296094676341903451088910514954478e-01 , +1.16078669527372101102128788451037300e+00 }, + {+8.31294185704192667429879293194971979e-01 , +1.19231087346512905437819601187290515e+00 }, + {+7.75279686135698531401772015669848770e-01 , +1.03342833983253459558199442655538342e+00 }, + {+8.63016425798844855066249692754354328e-01 , +1.30504558255929258800082186193362940e+00 }, + {-4.01121200686206158536606380948796868e-01 , -4.24984406726507784951210586374028211e-01 }, + {-7.26296876814658931564849808637518436e-01 , -9.20844916340064967501644052845522852e-01 }, + {-6.51251452570346556214531119621824473e-01 , -7.77468783732375082858572957021836038e-01 }, + {-3.38884333978433982537126212264411151e-01 , -3.52831574304372263524860719607206882e-01 }, + {+1.72392299987310537723317338532069698e-01 , +1.74131195612490806064041759672764899e-01 }, + {-5.51462898378643817665079041034914553e-01 , -6.20481084171861782978180187043219425e-01 }, + {-9.64904165725041895740332620334811509e-01 , -2.01255815534358709601688745544440155e+00 }, + {+4.86038064330916674826710277557140216e-01 , +5.30859816325958819695652448239234798e-01 }, + {-1.71510111227157313606994648580439389e-01 , -1.73222128192418256096782313598032358e-01 }, + {-6.40878189659797481425584919634275138e-01 , -7.59662611746376207791374637040653373e-01 }, + {-9.21153045478257270772814990777987987e-01 , -1.59658607015886876829589111732687615e+00 }, + {+9.25949497320595948934851548983715475e-01 , +1.62921352070439068555146795332724838e+00 }, + {+6.67107753751613463677472282142844051e-02 , +6.68100020655284551850585715148980622e-02 }, + {-7.11704678755497255338013928849250078e-01 , -8.90629832677982628857274943637335499e-01 }, + {+2.35257760530820814182106914813630283e-01 , +2.39748052543229836539708777609029562e-01 }, + {-5.99809116170998191108765240642242134e-01 , -6.92848977932719794607448570102282265e-01 }, + {+2.36856137326988402946881251409649849e-02 , +2.36900444994942435090279187572837691e-02 }, + {+4.79753735068501274696473046788014472e-01 , +5.22664335906700767137176554743504717e-01 }, + {-6.67813223642592657114391840877942741e-02 , -6.68808647504004031806529934863158282e-02 }, + {+4.94668485437996574560770568496081978e-01 , +5.42222516993971658066335656775147071e-01 }, + {+9.22379519394591618208778527332469821e-01 , +1.60474385650308389567794994129179836e+00 }, + {+8.66971245297548764874306925776181743e-01 , +1.32075371812223257408713093011711133e+00 }, + {-4.12983669850965462444492004578933120e-01 , -4.39203093212737172928023065922190938e-01 }, + {-5.47462091173417153555647018947638571e-01 , -6.14749985698478964136871989998405650e-01 }, + {+1.94666780724803700763914093840867281e-02 , +1.94691376077079831895420594672983760e-02 }, + {+7.50017550413333511372115935955662280e-01 , +9.72995190965114870405588062706564568e-01 }, + {+4.63499306097077345967250039393547922e-01 , +5.01758888267043269595832923648429311e-01 }, + {-8.37886773978241183868931329925544560e-01 , -1.21403834050977292529923613488719174e+00 }, + {-8.44836969187467534680990866036154330e-01 , -1.23783490656982630833439816713451784e+00 }, + {-6.25239150214307315422956889960914850e-02 , -6.26055803188625957299760516982282628e-02 }, + {+9.22830248275150921699605532921850681e-01 , +1.60777295202485947753242989620181993e+00 }, + {-4.00102773592881266750964641687460244e-01 , -4.23771285697601852943736383483221011e-01 }, + {-2.14797176230649089490043479599989951e-01 , -2.18195175029126913051736730604906575e-01 }, + {+8.02345358196084879054410521348472685e-01 , +1.10516139904369984294917445650114715e+00 }, + {-4.71866662797370439719202295236755162e-01 , -5.12468964220618253719339326228366982e-01 }, + {-5.92911187960187158196845302882138640e-01 , -6.82143591940237868513886526048905163e-01 }, + {-8.76501962797874289989863427763339132e-01 , -1.36046970086086594286889855837180352e+00 }, + {-7.98090332703508265055347692396026105e-01 , -1.09333002432404366596746493295830794e+00 }, + {-9.95891457501823618159164652752224356e-01 , -3.09288886654198972890077598351970075e+00 }, + {+8.90238675794242206862350030860397965e-01 , +1.42307507755399321220435398978827125e+00 }, + {+2.65017376806080395823528306209482253e-01 , +2.71497140326271882216423312414093371e-01 }, + {-8.86140475954999118357591214589774609e-01 , -1.40366125031104099709908824399414822e+00 }, + {-2.77209688130732434530045793508179486e-01 , -2.84656945620440202957754725962273832e-01 }, + {-5.61942305274404585446745841181837022e-01 , -6.35667384226952551579962809234573845e-01 }, + {+5.85899688875965107826004896196536720e-01 , +6.71399455223037749672225055315941521e-01 }, + {+2.89700970360319720597885861934628338e-01 , +2.98239807343473574150289333944139019e-01 }, + {+6.75650398573335864149669305334100500e-01 , +8.21067357862862215855289466859201333e-01 }, + {+8.44595640831392491243434506031917408e-01 , +1.23699243938765398083225288313951255e+00 }, + {+7.51976945151026199276600436860462651e-01 , +9.77489203652249672733902415356008597e-01 }, + {+5.84881385823573185511747851705877110e-01 , +6.69850275691608515819691751205471169e-01 }, + {+1.23322570031297651382828917121514678e-01 , +1.23953519549355101274007946363965461e-01 }, + {-2.47363663564394764549092542438302189e-01 , -2.52602687864803306420298300961589174e-01 }, + {-8.72948142869468535209875881264451891e-01 , -1.34533684420626699711543794108336721e+00 }, + {+8.66853617575293000641067919787019491e-01 , +1.32028029629708106765994282266545478e+00 }, + {+9.65229404828656933901243064610753208e-02 , +9.68243850830075059690451893341587506e-02 }, + {-5.83777226707343555034412929671816528e-01 , -6.68173646565246900863367519530400319e-01 }, + {+9.94603681374665704595372517360374331e-01 , +2.95624183430168683072844226874046298e+00 }, + {+7.54573357889273710341626610897947103e-01 , +9.83491456493016558037612443042858735e-01 }, + {-8.72065566686865079049084670259617269e-01 , -1.34163989421242748094845640631675400e+00 }, + {-1.57050652646726351946426802896894515e-01 , -1.58361317756145752747805962366582739e-01 }, + {+9.07524513496185902816648649604758248e-01 , +1.51330900778040915728163457702896072e+00 }, + {+8.88013640284071148478517443436430767e-01 , +1.41245174788920614220523066982061669e+00 }, + {+5.25920210223992712350593592418590561e-01 , +5.84488641604906625861481645606402185e-01 }, + {-8.55361269493308373768059027497656643e-01 , -1.27579779541753266418749325433063584e+00 }, + {+7.52458416629113502338555008464027196e-01 , +9.78598154541206783526807546834975210e-01 }, + {+2.11594554867804984787937883083941415e-01 , +2.14840051580673426585151516223475322e-01 }, + {-8.77614995063785929829691667691804469e-01 , -1.36529288845779405074788706705067271e+00 }, + {-8.13140811836136734314095519948750734e-01 , -1.13623027343457311746522665305747518e+00 }, + {-9.09625945665704094267312029842287302e-03 , -9.09651034981903612229998860206266111e-03 }, + {+7.11723260837115345722736492461990565e-01 , +8.90667489144918362129731310184000150e-01 }, + {-8.83729688568336113618784111167769879e-01 , -1.39254560811946769303745792066561506e+00 }, + {+2.81119771587539202251093684026272967e-01 , +2.88897516488748428420498810336047168e-01 }, + {+8.67969571335847511761585337808355689e-01 , +1.32478745650884654793737485679177607e+00 }, + {+3.71711284421433640901000217127148062e-01 , +3.90407276521447439801268385319531263e-01 }, + {-5.53264526894389874200896883849054575e-01 , -6.23073752400105167233817313285610373e-01 }, + {-7.50972436681106358946635737083852291e-01 , -9.75181502145488477599896301261990367e-01 }, + {+6.22632428081477007353328190220054239e-01 , +7.29292680370939598849165484001382202e-01 }, + {-5.93039523143406244187758602492976934e-01 , -6.82341523923040079633725976448706286e-01 }, + {-6.21804927349977942796499519317876548e-01 , -7.27942416494347815893737620840227454e-01 }, + {-5.91667199221579398482617762056179345e-01 , -6.80227384371192279205589233595913495e-01 }, + }; + + for (double[] testCase : testCases) { + failures += testAtanhCaseWithUlpDiff(testCase[0], + testCase[1], + 3.0); + } + + return failures; + } + + public static int testAtanhCaseWithTolerance(double input, + double expected, + double tolerance) { + int failures = 0; + failures += Tests.testTolerance("Math.atanh", input, Math::atanh, expected, tolerance); + failures += Tests.testTolerance("Math.atanh", -input, Math::atanh, -expected, tolerance); + + failures += Tests.testTolerance("StrictMath.atanh", input, StrictMath::atanh, expected, tolerance); + failures += Tests.testTolerance("StrictMath.atanh", -input, StrictMath::atanh, -expected, tolerance); + return failures; + } + + public static int testAtanhCaseWithUlpDiff(double input, + double expected, + double ulps) { + int failures = 0; + + failures += Tests.testUlpDiffWithAbsBound("Math.atanh", input, Math::atanh, expected, ulps, Double.POSITIVE_INFINITY); + failures += Tests.testUlpDiffWithAbsBound("Math.atanh", -input, Math::atanh, -expected, ulps, Double.POSITIVE_INFINITY); + + failures += Tests.testUlpDiffWithAbsBound("StrictMath.atanh", input, StrictMath::atanh, expected, ulps, Double.POSITIVE_INFINITY); + failures += Tests.testUlpDiffWithAbsBound("StrictMath.atanh", -input, StrictMath::atanh, -expected, ulps, Double.POSITIVE_INFINITY); + return failures; + } } diff --git a/test/jdk/java/lang/StrictMath/ExhaustingTests.java b/test/jdk/java/lang/StrictMath/ExhaustingTests.java index d028f0541fa..23b55a50bee 100644 --- a/test/jdk/java/lang/StrictMath/ExhaustingTests.java +++ b/test/jdk/java/lang/StrictMath/ExhaustingTests.java @@ -94,6 +94,7 @@ public class ExhaustingTests { new UnaryTestCase("asinh", FdlibmTranslit::asinh, StrictMath::asinh, DEFAULT_SHIFT), new UnaryTestCase("acosh", FdlibmTranslit::acosh, StrictMath::acosh, DEFAULT_SHIFT), + new UnaryTestCase("atanh", FdlibmTranslit::atanh, StrictMath::atanh, DEFAULT_SHIFT), }; for (var testCase : testCases) { diff --git a/test/jdk/java/lang/StrictMath/FdlibmTranslit.java b/test/jdk/java/lang/StrictMath/FdlibmTranslit.java index 6ac90c826d5..ffa18676c90 100644 --- a/test/jdk/java/lang/StrictMath/FdlibmTranslit.java +++ b/test/jdk/java/lang/StrictMath/FdlibmTranslit.java @@ -148,6 +148,10 @@ public class FdlibmTranslit { return Acosh.compute(x); } + public static double atanh(double x) { + return Atanh.compute(x); + } + public static double IEEEremainder(double f1, double f2) { return IEEEremainder.compute(f1, f2); } @@ -2841,4 +2845,50 @@ public class FdlibmTranslit { } } } + + /* + * Return the Inverse Hyperbolic Tangent of x + * + * Method : + * 1.Reduced x to positive by atanh(-x) = -atanh(x) + * 2.For x>=0.5 + * 1 2x x + * atanh(x) = --- * log(1 + -------) = 0.5 * log1p(2 * --------) + * 2 1 - x 1 - x + * + * For x<0.5 + * atanh(x) = 0.5*log1p(2x+2x*x/(1-x)) + * + * Special cases: + * atanh(x) is NaN if |x| > 1 with signal; + * atanh(NaN) is that NaN with no signal; + * atanh(+-1) is +-INF with signal. + * + */ + private static final class Atanh { + private static final double zero = 0.0; + private static final double one = 1.0; + private static final double huge = 1.0e300; + + static double compute(double x) { + double t; + int hx,ix; + /*unsigned*/ int lx; + hx = __HI(x); /* high word */ + lx = __LO(x); /* low word */ + ix = hx&0x7fffffff; + if ((ix|((lx|(-lx))>>>31))>0x3ff00000) /* |x|>1 */ + return (x-x)/(x-x); + if(ix==0x3ff00000) + return x/zero; + if(ix<0x3e300000&&(huge+x)>zero) return x; /* x<2**-28 */ + x = __HI(x, ix); /* x <- |x| */ + if(ix<0x3fe00000) { /* x < 0.5 */ + t = x+x; + t = 0.5*log1p(t+t*x/(one-x)); + } else + t = 0.5*log1p((x+x)/(one-x)); + if(hx>=0) return t; else return -t; + } + } } diff --git a/test/jdk/java/lang/StrictMath/HyperbolicTests.java b/test/jdk/java/lang/StrictMath/HyperbolicTests.java index b13a16011c1..4be1b350c1d 100644 --- a/test/jdk/java/lang/StrictMath/HyperbolicTests.java +++ b/test/jdk/java/lang/StrictMath/HyperbolicTests.java @@ -34,7 +34,7 @@ import java.util.function.DoubleUnaryOperator; * @build FdlibmTranslit * @build HyperbolicTests * @run main HyperbolicTests - * @summary Tests for StrictMath.{sinh, cosh, tanh, asinh, acosh} + * @summary Tests for StrictMath.{sinh, cosh, tanh, asinh, acosh, atanh} */ /** @@ -60,12 +60,14 @@ public class HyperbolicTests { failures += testAgainstTranslitSinh(); failures += testAgainstTranslitCosh(); failures += testAgainstTranslitTanh(); + failures += testAgainstTranslitAtanh(); failures += testSinh(); failures += testCosh(); failures += testTanh(); failures += testAsinh(); failures += testAcosh(); + failures += testAtanh(); if (failures > 0) { System.err.println("Testing the hyperbolics incurred " @@ -82,7 +84,8 @@ public class HyperbolicTests { COSH(HyperbolicTests::testCoshCase, FdlibmTranslit::cosh), TANH(HyperbolicTests::testTanhCase, FdlibmTranslit::tanh), ASINH(HyperbolicTests::testAsinhCase, FdlibmTranslit::asinh), - ACOSH(HyperbolicTests::testAcoshCase, FdlibmTranslit::acosh); + ACOSH(HyperbolicTests::testAcoshCase, FdlibmTranslit::acosh), + ATANH(HyperbolicTests::testAtanhCase, FdlibmTranslit::atanh); private DoubleDoubleToInt testCase; private DoubleUnaryOperator transliteration; @@ -216,6 +219,31 @@ public class HyperbolicTests { return failures; } + /** + * Test StrictMath.tanh against transliteration port of tanh + */ + private static int testAgainstTranslitAtanh() { + int failures = 0; + double x; + + // Probe near decision points in the FDLIBM algorithm. + double[] decisionPoints = { + 0.0, + + 0x1.0p-28, + -0x1.0p-28, + + 1.0, + -1.0, + }; + + for (double testPoint : decisionPoints) { + failures += testRangeMidpoint(testPoint, Math.ulp(testPoint), 1000, HyperbolicTest.ATANH); + } + + return failures; + } + private interface DoubleDoubleToInt { int apply(double x, double y); } @@ -267,6 +295,11 @@ public class HyperbolicTests { StrictMath::acosh, expected); } + private static int testAtanhCase(double input, double expected) { + return Tests.test("StrictMath.atanh(double)", input, + StrictMath::atanh, expected); + } + private static int testSinh() { int failures = 0; double [][] testCases = { @@ -618,4 +651,69 @@ public class HyperbolicTests { return failures; } + + private static int testAtanh() { + int failures = 0; + double [][] testCases = { + {0x1.5798ee2308c36p-27, 0x1.5798ee2308c37p-27}, + {0x1.ffffffffffffep-26, 0x1p-25}, + {0x1.ffffffffffffep-25, 0x1.0000000000004p-24}, + {0x1.ad7f29abcaf47p-24, 0x1.ad7f29abcaf6p-24}, + {0x1.ad7f29abcaf48p-24, 0x1.ad7f29abcaf61p-24}, + {0x1.ffffffffffffep-24, 0x1.0000000000014p-23}, + {0x1.ffffffffffffep-23, 0x1.0000000000054p-22}, + {0x1.ffffffffffffep-22, 0x1.0000000000154p-21}, + {0x1.ffffffffffffep-21, 0x1.0000000000554p-20}, + {0x1.0c6f7a0b5ed8dp-20, 0x1.0c6f7a0b5f3b3p-20}, + {0x1.ffffffffffffep-20, 0x1.0000000001554p-19}, + {0x1.ffffffffffffep-19, 0x1.0000000005554p-18}, + {0x1.fffffffffffffp-18, 0x1.0000000015555p-17}, + {0x1p-17, 0x1.0000000015555p-17}, + {0x1.4f8b588e368edp-17, 0x1.4f8b588e6698bp-17}, + {0x1.fffffffffffffp-17, 0x1.0000000055555p-16}, + {0x1.fffffffffffffp-16, 0x1.0000000155555p-15}, + {0x1p-15, 0x1.0000000155555p-15}, + {0x1.fffffffffe5ddp-15, 0x1.0000000554844p-14}, + {0x1.fffffffffffffp-15, 0x1.0000000555555p-14}, + {0x1.a36e2eb1c432dp-14, 0x1.a36e2ec938ff8p-14}, + {0x1.ffffffffffffep-14, 0x1.0000001555555p-13}, + {0x1p-13, 0x1.0000001555556p-13}, + {0x1.ffffffffffd51p-13, 0x1.0000005555401p-12}, + {0x1.fffffffffffffp-13, 0x1.0000005555559p-12}, + {0x1.ffffffffffffep-12, 0x1.0000015555587p-11}, + {0x1p-11, 0x1.0000015555588p-11}, + {0x1.fffffffffff1p-11, 0x1.0000055555811p-10}, + {0x1p-10, 0x1.0000055555889p-10}, + {0x1.0624dd2f1a9c6p-10, 0x1.0624e2e91ece1p-10}, + {0x1.0624dd2f1a9f8p-10, 0x1.0624e2e91ed13p-10}, + {0x1.fffffffffffddp-10, 0x1.0000155558877p-9}, + {0x1.fffffffffffffp-10, 0x1.0000155558888p-9}, + {0x1.ffffffffffffcp-9, 0x1.0000555588889p-8}, + {0x1.ffffffffffffep-9, 0x1.000055558888ap-8}, + {0x1.ffffffffffff8p-8, 0x1.0001555888917p-7}, + {0x1.ffffffffffffep-8, 0x1.000155588891ap-7}, + {0x1.47ae147ae1458p-7, 0x1.47b0e059d0574p-7}, + {0x1.47ae147ae1464p-7, 0x1.47b0e059d058p-7}, + {0x1.ffffffffffffep-7, 0x1.000555888ad1bp-6}, + {0x1.fffffffffffffp-7, 0x1.000555888ad1cp-6}, + {0x1.ffffffffffff9p-6, 0x1.001558891aedep-5}, + {0x1.ffffffffffffep-6, 0x1.001558891aee1p-5}, + {0x1.ffffffffffff9p-5, 0x1.005588ad375a9p-4}, + {0x1.fffffffffffffp-5, 0x1.005588ad375acp-4}, + {0x1.9999999999996p-4, 0x1.9af93cd23440ep-4}, + {0x1.9999999999997p-4, 0x1.9af93cd23440fp-4}, + {0x1.fffffffffffffp-4, 0x1.015891c9eaef7p-3}, + {0x1p-3, 0x1.015891c9eaef7p-3}, + {0x1.fffffffffffffp-3, 0x1.058aefa811451p-2}, + {0x1.ffffffffffffcp-2, 0x1.193ea7aad0308p-1}, + {0x1.ffffffffffffep-2, 0x1.193ea7aad0309p-1}, + {0x1.ffffffffffffbp-1, 0x1.1e9067763b478p+4}, + {0x1.ffffffffffffep-1, 0x1.25e4f7b2737fap+4}, + }; + + for (double[] testCase: testCases) + failures += testAtanhCase(testCase[0], testCase[1]); + + return failures; + } }