From 8938126d1bb5b9aaf8b07e5eb4afa4cc5e70150a Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Fri, 29 Aug 2014 09:32:51 +0000 Subject: [PATCH] Port fdlibm implementation for Math.sinh. R=rtoy@chromium.org BUG=v8:3493 LOG=N Review URL: https://codereview.chromium.org/488003005 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23512 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/math.js | 10 +------- test/mjsunit/es6/math-hyperbolic.js | 48 ++++++++++++++++++++++++++++++------ third_party/fdlibm/fdlibm.cc | 3 ++- third_party/fdlibm/fdlibm.h | 2 +- third_party/fdlibm/fdlibm.js | 49 +++++++++++++++++++++++++++++++++++++ tools/generate-runtime-tests.py | 2 +- 6 files changed, 94 insertions(+), 20 deletions(-) diff --git a/src/math.js b/src/math.js index 7ead7a8..008a41e 100644 --- a/src/math.js +++ b/src/math.js @@ -186,14 +186,6 @@ function MathTrunc(x) { return x; } -// ES6 draft 09-27-13, section 20.2.2.30. -function MathSinh(x) { - if (!IS_NUMBER(x)) x = NonNumberToNumber(x); - // Idempotent for NaN, +/-0 and +/-Infinity. - if (x === 0 || !NUMBER_IS_FINITE(x)) return x; - return (MathExp(x) - MathExp(-x)) / 2; -} - // ES6 draft 09-27-13, section 20.2.2.12. function MathCosh(x) { if (!IS_NUMBER(x)) x = NonNumberToNumber(x); @@ -376,7 +368,7 @@ function SetUpMath() { "imul", MathImul, "sign", MathSign, "trunc", MathTrunc, - "sinh", MathSinh, + "sinh", MathSinh, // implemented by third_party/fdlibm "cosh", MathCosh, "tanh", MathTanh, "asinh", MathAsinh, diff --git a/test/mjsunit/es6/math-hyperbolic.js b/test/mjsunit/es6/math-hyperbolic.js index 1ceb951..1632e00 100644 --- a/test/mjsunit/es6/math-hyperbolic.js +++ b/test/mjsunit/es6/math-hyperbolic.js @@ -25,6 +25,9 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// TODO(3468): we rely on a precise Math.exp. +// Flags: --no-fast-math + [Math.sinh, Math.cosh, Math.tanh, Math.asinh, Math.acosh, Math.atanh]. forEach(function(fun) { assertTrue(isNaN(fun(NaN))); @@ -66,14 +69,14 @@ function test_id(fun, rev, value) { }); -assertEquals("Infinity", String(Math.cosh(-Infinity))); -assertEquals("Infinity", String(Math.cosh(Infinity))); -assertEquals("Infinity", String(Math.cosh("-Infinity"))); -assertEquals("Infinity", String(Math.cosh("Infinity"))); +assertEquals(Infinity, Math.cosh(-Infinity)); +assertEquals(Infinity, Math.cosh(Infinity)); +assertEquals(Infinity, Math.cosh("-Infinity")); +assertEquals(Infinity, Math.cosh("Infinity")); -assertEquals("-Infinity", String(Math.atanh(-1))); -assertEquals("Infinity", String(Math.atanh(1))); +assertEquals(-Infinity, Math.atanh(-1)); +assertEquals(Infinity, Math.atanh(1)); // Math.atanh(x) is NaN for |x| > 1 and NaN [1.000000000001, Math.PI, 10000000, 2, Infinity, NaN].forEach(function(x) { @@ -82,6 +85,8 @@ assertEquals("Infinity", String(Math.atanh(1))); }); +assertEquals(0, Math.sinh(0)); +assertEquals(-Infinity, 1/Math.sinh(-0)); assertEquals(1, Math.tanh(Infinity)); assertEquals(-1, Math.tanh(-Infinity)); assertEquals(1, Math.cosh(0)); @@ -97,9 +102,7 @@ assertEquals("Infinity", String(Math.acosh(Infinity))); // Some random samples. -assertEqualsDelta(0.5210953054937, Math.sinh(0.5), 1E-12); assertEqualsDelta(74.203210577788, Math.sinh(5), 1E-12); -assertEqualsDelta(-0.5210953054937, Math.sinh(-0.5), 1E-12); assertEqualsDelta(-74.203210577788, Math.sinh(-5), 1E-12); assertEqualsDelta(1.1276259652063, Math.cosh(0.5), 1E-12); @@ -134,3 +137,32 @@ assertEqualsDelta(-0.1003353477311, Math.atanh(-0.1), 1E-12); [1-(1E-16), 0, 1E-10, 1E-50].forEach(function(x) { assertEqualsDelta(Math.atanh(x), -Math.atanh(-x), 1E-12); }); + + +// Implementation-specific tests for sinh. +// Case |x| < 2^-28 +assertEquals(Math.pow(2, -29), Math.sinh(Math.pow(2, -29))); +assertEquals(-Math.pow(2, -29), Math.sinh(-Math.pow(2, -29))); +// Case |x| < 1 +assertEquals(0.5210953054937474, Math.sinh(0.5)); +assertEquals(-0.5210953054937474, Math.sinh(-0.5)); +// sinh(10*log(2)) = 1048575/2048, case |x| < 22 +assertEquals(1048575/2048, Math.sinh(10*Math.LN2)); +assertEquals(-1048575/2048, Math.sinh(-10*Math.LN2)); +// Case |x| < 22 +assertEquals(11013.232874703393, Math.sinh(10)); +assertEquals(-11013.232874703393, Math.sinh(-10)); +// Case |x| in [22, log(maxdouble)] +assertEquals(2.1474836479999983e9, Math.sinh(32*Math.LN2)); +assertEquals(-2.1474836479999983e9, Math.sinh(-32*Math.LN2)); +// Case |x| in [22, log(maxdouble)] +assertEquals(1.3440585709080678e43, Math.sinh(100)); +assertEquals(-1.3440585709080678e43, Math.sinh(-100)); +// No overflow, case |x| in [log(maxdouble), threshold] +assertEquals(1.7976931348621744e308, Math.sinh(710.4758600739439)); +assertEquals(-1.7976931348621744e308, Math.sinh(-710.4758600739439)); +// Overflow, case |x| > threshold +assertEquals(Infinity, Math.sinh(710.475860073944)); +assertEquals(-Infinity, Math.sinh(-710.475860073944)); +assertEquals(Infinity, Math.sinh(1000)); +assertEquals(-Infinity, Math.sinh(-1000)); diff --git a/third_party/fdlibm/fdlibm.cc b/third_party/fdlibm/fdlibm.cc index a92d5c3..67c1b72 100644 --- a/third_party/fdlibm/fdlibm.cc +++ b/third_party/fdlibm/fdlibm.cc @@ -78,7 +78,8 @@ const double MathConstants::constants[] = { 1.58730158725481460165e-03, // 48 -7.93650757867487942473e-05, // 49 4.00821782732936239552e-06, // 50 - -2.01099218183624371326e-07 // Q5 51 + -2.01099218183624371326e-07, // Q5 51 + 710.4758600739439 // 52 overflow threshold for sinh }; diff --git a/third_party/fdlibm/fdlibm.h b/third_party/fdlibm/fdlibm.h index e4e0f21..cadf85b 100644 --- a/third_party/fdlibm/fdlibm.h +++ b/third_party/fdlibm/fdlibm.h @@ -23,7 +23,7 @@ int rempio2(double x, double* y); // Constants to be exposed to builtins via Float64Array. struct MathConstants { - static const double constants[52]; + static const double constants[53]; }; } } // namespace v8::internal diff --git a/third_party/fdlibm/fdlibm.js b/third_party/fdlibm/fdlibm.js index 9bdd979..95ea5f4 100644 --- a/third_party/fdlibm/fdlibm.js +++ b/third_party/fdlibm/fdlibm.js @@ -710,3 +710,52 @@ function MathExpm1(x) { } return y; } + + +// ES6 draft 09-27-13, section 20.2.2.30. +// Math.sinh +// 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 +Infinity, -Infinity, or NaN. +// only sinh(0)=0 is exact for finite x. +// +const KSINH_OVERFLOW = kMath[52]; +const TWO_M28 = 3.725290298461914e-9; // 2^-28, empty lower half +const LOG_MAXD = 709.7822265625; // 0x40862e42 00000000, empty lower half + +function MathSinh(x) { + x = x * 1; // Convert to number. + var h = (x < 0) ? -0.5 : 0.5; + // |x| in [0, 22]. return sign(x)*0.5*(E+E/(E+1)) + var ax = MathAbs(x); + if (ax < 22) { + // For |x| < 2^-28, sinh(x) = x + if (ax < TWO_M28) return x; + var t = MathExpm1(ax); + if (ax < 1) return h * (2 * t - t * t / (t + 1)); + return h * (t + t / (t + 1)); + } + // |x| in [22, log(maxdouble)], return 0.5 * exp(|x|) + if (ax < LOG_MAXD) return h * MathExp(ax); + // |x| in [log(maxdouble), overflowthreshold] + // overflowthreshold = 710.4758600739426 + if (ax <= KSINH_OVERFLOW) { + var w = MathExp(0.5 * ax); + var t = h * w; + return t * w; + } + // |x| > overflowthreshold or is NaN. + // Return Infinity of the appropriate sign or NaN. + return x * INFINITY; +} diff --git a/tools/generate-runtime-tests.py b/tools/generate-runtime-tests.py index 50970b4..7efcf8c 100755 --- a/tools/generate-runtime-tests.py +++ b/tools/generate-runtime-tests.py @@ -51,7 +51,7 @@ EXPECTED_FUNCTION_COUNT = 431 EXPECTED_FUZZABLE_COUNT = 332 EXPECTED_CCTEST_COUNT = 7 EXPECTED_UNKNOWN_COUNT = 17 -EXPECTED_BUILTINS_COUNT = 808 +EXPECTED_BUILTINS_COUNT = 807 # Don't call these at all. -- 2.7.4