Harmony: implement Math.cbrt, Math.expm1 and Math.log1p.
BUG=v8:2938 LOG=N R=jarin@chromium.org Review URL: https://codereview.chromium.org/163563003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19486 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
76bdb032ac
commit
84cf85598d
@ -174,6 +174,24 @@ function MathClz32(x) {
|
||||
}
|
||||
|
||||
|
||||
//ES6 draft 09-27-13, section 20.2.2.9.
|
||||
function MathCbrt(x) {
|
||||
return %Math_cbrt(TO_NUMBER_INLINE(x));
|
||||
}
|
||||
|
||||
|
||||
//ES6 draft 09-27-13, section 20.2.2.14.
|
||||
function MathExpm1(x) {
|
||||
return %Math_expm1(TO_NUMBER_INLINE(x));
|
||||
}
|
||||
|
||||
|
||||
//ES6 draft 09-27-13, section 20.2.2.20.
|
||||
function MathLog1p(x) {
|
||||
return %Math_log1p(TO_NUMBER_INLINE(x));
|
||||
}
|
||||
|
||||
|
||||
function ExtendMath() {
|
||||
%CheckIsBootstrapping();
|
||||
|
||||
@ -191,7 +209,10 @@ function ExtendMath() {
|
||||
"log2", MathLog2,
|
||||
"hypot", MathHypot,
|
||||
"fround", MathFround,
|
||||
"clz32", MathClz32
|
||||
"clz32", MathClz32,
|
||||
"cbrt", MathCbrt,
|
||||
"log1p", MathLog1p,
|
||||
"expm1", MathExpm1
|
||||
));
|
||||
}
|
||||
|
||||
|
119
src/runtime.cc
119
src/runtime.cc
@ -7647,33 +7647,110 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringCompare) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_acos) {
|
||||
SealHandleScope shs(isolate);
|
||||
ASSERT(args.length() == 1);
|
||||
isolate->counters()->math_acos()->Increment();
|
||||
#define RUNTIME_UNARY_MATH(NAME) \
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_##NAME) { \
|
||||
SealHandleScope shs(isolate); \
|
||||
ASSERT(args.length() == 1); \
|
||||
isolate->counters()->math_##NAME()->Increment(); \
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0); \
|
||||
return isolate->heap()->AllocateHeapNumber(std::NAME(x)); \
|
||||
}
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
return isolate->heap()->AllocateHeapNumber(std::acos(x));
|
||||
RUNTIME_UNARY_MATH(acos)
|
||||
RUNTIME_UNARY_MATH(asin)
|
||||
RUNTIME_UNARY_MATH(atan)
|
||||
RUNTIME_UNARY_MATH(log)
|
||||
#undef RUNTIME_UNARY_MATH
|
||||
|
||||
|
||||
// Cube root approximation, refer to: http://metamerist.com/cbrt/cbrt.htm
|
||||
// Using initial approximation adapted from Kahan's cbrt and 4 iterations
|
||||
// of Newton's method.
|
||||
inline double CubeRootNewtonIteration(double approx, double x) {
|
||||
return (1.0 / 3.0) * (x / (approx * approx) + 2 * approx);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_asin) {
|
||||
SealHandleScope shs(isolate);
|
||||
ASSERT(args.length() == 1);
|
||||
isolate->counters()->math_asin()->Increment();
|
||||
inline double CubeRoot(double x) {
|
||||
static const uint64_t magic = V8_2PART_UINT64_C(0x2A9F7893, 00000000);
|
||||
uint64_t xhigh = double_to_uint64(x);
|
||||
double approx = uint64_to_double(xhigh / 3 + magic);
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
return isolate->heap()->AllocateHeapNumber(std::asin(x));
|
||||
approx = CubeRootNewtonIteration(approx, x);
|
||||
approx = CubeRootNewtonIteration(approx, x);
|
||||
approx = CubeRootNewtonIteration(approx, x);
|
||||
return CubeRootNewtonIteration(approx, x);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_atan) {
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_cbrt) {
|
||||
SealHandleScope shs(isolate);
|
||||
ASSERT(args.length() == 1);
|
||||
isolate->counters()->math_atan()->Increment();
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
return isolate->heap()->AllocateHeapNumber(std::atan(x));
|
||||
if (x == 0 || std::isinf(x)) return args[0];
|
||||
double result = (x > 0) ? CubeRoot(x) : -CubeRoot(-x);
|
||||
return isolate->heap()->AllocateHeapNumber(result);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_log1p) {
|
||||
SealHandleScope shs(isolate);
|
||||
ASSERT(args.length() == 1);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
|
||||
double x_abs = std::fabs(x);
|
||||
// Use Taylor series to approximate. With y = x + 1;
|
||||
// log(y) at 1 == log(1) + log'(1)(y-1)/1! + log''(1)(y-1)^2/2! + ...
|
||||
// == 0 + x - x^2/2 + x^3/3 ...
|
||||
// The closer x is to 0, the fewer terms are required.
|
||||
static const double threshold_2 = 1.0 / 0x00800000;
|
||||
static const double threshold_3 = 1.0 / 0x00008000;
|
||||
static const double threshold_7 = 1.0 / 0x00000080;
|
||||
|
||||
double result;
|
||||
if (x_abs < threshold_2) {
|
||||
result = x * (1.0/1.0 - x * 1.0/2.0);
|
||||
} else if (x_abs < threshold_3) {
|
||||
result = x * (1.0/1.0 - x * (1.0/2.0 - x * (1.0/3.0)));
|
||||
} else if (x_abs < threshold_7) {
|
||||
result = x * (1.0/1.0 - x * (1.0/2.0 - x * (
|
||||
1.0/3.0 - x * (1.0/4.0 - x * (
|
||||
1.0/5.0 - x * (1.0/6.0 - x * (
|
||||
1.0/7.0)))))));
|
||||
} else { // Use regular log if not close enough to 0.
|
||||
result = std::log(1.0 + x);
|
||||
}
|
||||
return isolate->heap()->AllocateHeapNumber(result);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_expm1) {
|
||||
SealHandleScope shs(isolate);
|
||||
ASSERT(args.length() == 1);
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
|
||||
double x_abs = std::fabs(x);
|
||||
// Use Taylor series to approximate.
|
||||
// exp(x) - 1 at 0 == -1 + exp(0) + exp'(0)*x/1! + exp''(0)*x^2/2! + ...
|
||||
// == x/1! + x^2/2! + x^3/3! + ...
|
||||
// The closer x is to 0, the fewer terms are required.
|
||||
static const double threshold_2 = 1.0 / 0x00400000;
|
||||
static const double threshold_3 = 1.0 / 0x00004000;
|
||||
static const double threshold_6 = 1.0 / 0x00000040;
|
||||
|
||||
double result;
|
||||
if (x_abs < threshold_2) {
|
||||
result = x * (1.0/1.0 + x * (1.0/2.0));
|
||||
} else if (x_abs < threshold_3) {
|
||||
result = x * (1.0/1.0 + x * (1.0/2.0 + x * (1.0/6.0)));
|
||||
} else if (x_abs < threshold_6) {
|
||||
result = x * (1.0/1.0 + x * (1.0/2.0 + x * (
|
||||
1.0/6.0 + x * (1.0/24.0 + x * (
|
||||
1.0/120.0 + x * (1.0/720.0))))));
|
||||
} else { // Use regular exp if not close enough to 0.
|
||||
result = std::exp(x) - 1.0;
|
||||
}
|
||||
return isolate->heap()->AllocateHeapNumber(result);
|
||||
}
|
||||
|
||||
|
||||
@ -7724,16 +7801,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_floor) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_log) {
|
||||
SealHandleScope shs(isolate);
|
||||
ASSERT(args.length() == 1);
|
||||
isolate->counters()->math_log()->Increment();
|
||||
|
||||
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
|
||||
return isolate->heap()->AllocateHeapNumber(std::log(x));
|
||||
}
|
||||
|
||||
|
||||
// Slow version of Math.pow. We check for fast paths for special cases.
|
||||
// Used if SSE2/VFP3 is not available.
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_pow) {
|
||||
|
@ -177,14 +177,17 @@ namespace internal {
|
||||
F(Math_acos, 1, 1) \
|
||||
F(Math_asin, 1, 1) \
|
||||
F(Math_atan, 1, 1) \
|
||||
F(Math_atan2, 2, 1) \
|
||||
F(Math_log, 1, 1) \
|
||||
F(Math_cbrt, 1, 1) \
|
||||
F(Math_log1p, 1, 1) \
|
||||
F(Math_expm1, 1, 1) \
|
||||
F(Math_sqrt, 1, 1) \
|
||||
F(Math_exp, 1, 1) \
|
||||
F(Math_floor, 1, 1) \
|
||||
F(Math_log, 1, 1) \
|
||||
F(Math_pow, 2, 1) \
|
||||
F(Math_pow_cfunction, 2, 1) \
|
||||
F(Math_atan2, 2, 1) \
|
||||
F(RoundNumber, 1, 1) \
|
||||
F(Math_sqrt, 1, 1) \
|
||||
F(Math_fround, 1, 1) \
|
||||
\
|
||||
/* Regular expressions */ \
|
||||
|
27
test/mjsunit/harmony/math-cbrt.js
Normal file
27
test/mjsunit/harmony/math-cbrt.js
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-maths
|
||||
|
||||
assertTrue(isNaN(Math.cbrt(NaN)));
|
||||
assertTrue(isNaN(Math.cbrt(function() {})));
|
||||
assertTrue(isNaN(Math.cbrt({ toString: function() { return NaN; } })));
|
||||
assertTrue(isNaN(Math.cbrt({ valueOf: function() { return "abc"; } })));
|
||||
assertEquals("Infinity", String(1/Math.cbrt(0)));
|
||||
assertEquals("-Infinity", String(1/Math.cbrt(-0)));
|
||||
assertEquals("Infinity", String(Math.cbrt(Infinity)));
|
||||
assertEquals("-Infinity", String(Math.cbrt(-Infinity)));
|
||||
|
||||
for (var i = 1E-100; i < 1E100; i *= Math.PI) {
|
||||
assertEqualsDelta(i, Math.cbrt(i*i*i), i * 1E-15);
|
||||
}
|
||||
|
||||
for (var i = -1E-100; i > -1E100; i *= Math.E) {
|
||||
assertEqualsDelta(i, Math.cbrt(i*i*i), -i * 1E-15);
|
||||
}
|
||||
|
||||
// Let's be exact at least for small integers.
|
||||
for (var i = 2; i < 10000; i++) {
|
||||
assertEquals(i, Math.cbrt(i*i*i));
|
||||
}
|
38
test/mjsunit/harmony/math-expm1.js
Normal file
38
test/mjsunit/harmony/math-expm1.js
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-maths --no-fast-math
|
||||
|
||||
assertTrue(isNaN(Math.expm1(NaN)));
|
||||
assertTrue(isNaN(Math.expm1(function() {})));
|
||||
assertTrue(isNaN(Math.expm1({ toString: function() { return NaN; } })));
|
||||
assertTrue(isNaN(Math.expm1({ valueOf: function() { return "abc"; } })));
|
||||
assertEquals("Infinity", String(1/Math.expm1(0)));
|
||||
assertEquals("-Infinity", String(1/Math.expm1(-0)));
|
||||
assertEquals("Infinity", String(Math.expm1(Infinity)));
|
||||
assertEquals(-1, Math.expm1(-Infinity));
|
||||
|
||||
for (var x = 0.1; x < 700; x += 0.1) {
|
||||
var expected = Math.exp(x) - 1;
|
||||
assertEqualsDelta(expected, Math.expm1(x), expected * 1E-14);
|
||||
expected = Math.exp(-x) - 1;
|
||||
assertEqualsDelta(expected, Math.expm1(-x), -expected * 1E-14);
|
||||
}
|
||||
|
||||
// Values close to 0:
|
||||
// Use six terms of Taylor expansion at 0 for exp(x) as test expectation:
|
||||
// exp(x) - 1 == exp(0) + exp(0) * x + x * x / 2 + ... - 1
|
||||
// == x + x * x / 2 + x * x * x / 6 + ...
|
||||
function expm1(x) {
|
||||
return x * (1 + x * (1/2 + x * (
|
||||
1/6 + x * (1/24 + x * (
|
||||
1/120 + x * (1/720 + x * (
|
||||
1/5040 + x * (1/40320 + x*(
|
||||
1/362880 + x * (1/3628800))))))))));
|
||||
}
|
||||
|
||||
for (var x = 1E-1; x > 1E-300; x *= 0.8) {
|
||||
var expected = expm1(x);
|
||||
assertEqualsDelta(expected, Math.expm1(x), expected * 1E-14);
|
||||
}
|
41
test/mjsunit/harmony/math-log1p.js
Normal file
41
test/mjsunit/harmony/math-log1p.js
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-maths
|
||||
|
||||
assertTrue(isNaN(Math.log1p(NaN)));
|
||||
assertTrue(isNaN(Math.log1p(function() {})));
|
||||
assertTrue(isNaN(Math.log1p({ toString: function() { return NaN; } })));
|
||||
assertTrue(isNaN(Math.log1p({ valueOf: function() { return "abc"; } })));
|
||||
assertEquals("Infinity", String(1/Math.log1p(0)));
|
||||
assertEquals("-Infinity", String(1/Math.log1p(-0)));
|
||||
assertEquals("Infinity", String(Math.log1p(Infinity)));
|
||||
assertEquals("-Infinity", String(Math.log1p(-1)));
|
||||
assertTrue(isNaN(Math.log1p(-2)));
|
||||
assertTrue(isNaN(Math.log1p(-Infinity)));
|
||||
|
||||
for (var x = 1E300; x > 1E-1; x *= 0.8) {
|
||||
var expected = Math.log(x + 1);
|
||||
assertEqualsDelta(expected, Math.log1p(x), expected * 1E-14);
|
||||
}
|
||||
|
||||
// Values close to 0:
|
||||
// Use Taylor expansion at 1 for log(x) as test expectation:
|
||||
// log1p(x) == log(x + 1) == 0 + x / 1 - x^2 / 2 + x^3 / 3 - ...
|
||||
function log1p(x) {
|
||||
var terms = [];
|
||||
var prod = x;
|
||||
for (var i = 1; i <= 20; i++) {
|
||||
terms.push(prod / i);
|
||||
prod *= -x;
|
||||
}
|
||||
var sum = 0;
|
||||
while (terms.length > 0) sum += terms.pop();
|
||||
return sum;
|
||||
}
|
||||
|
||||
for (var x = 1E-1; x > 1E-300; x *= 0.8) {
|
||||
var expected = log1p(x);
|
||||
assertEqualsDelta(expected, Math.log1p(x), expected * 1E-14);
|
||||
}
|
Loading…
Reference in New Issue
Block a user