Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(429)

Unified Diff: src/math.js

Issue 303753002: Trigonometric functions using fdlibm. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: test case Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/bootstrapper.cc ('k') | src/rempio2.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/math.js
diff --git a/src/math.js b/src/math.js
index d231c22ea2222c237914d72d6d60027fd40e618b..2052175d774cda879f58cbcf4dc7df05142d1af6 100644
--- a/src/math.js
+++ b/src/math.js
@@ -56,12 +56,6 @@ function MathCeil(x) {
return -MathFloor(-x);
}
-// ECMA 262 - 15.8.2.7
-function MathCos(x) {
- x = MathAbs(x); // Convert to number and get rid of -0.
- return TrigonometricInterpolation(x, 1);
-}
-
// ECMA 262 - 15.8.2.8
function MathExp(x) {
return %MathExpRT(TO_NUMBER_INLINE(x));
@@ -164,94 +158,332 @@ function MathRound(x) {
return %RoundNumber(TO_NUMBER_INLINE(x));
}
-// ECMA 262 - 15.8.2.16
-function MathSin(x) {
- x = x * 1; // Convert to number and deal with -0.
- if (%_IsMinusZero(x)) return x;
- return TrigonometricInterpolation(x, 0);
-}
-
// ECMA 262 - 15.8.2.17
function MathSqrt(x) {
return %_MathSqrtRT(TO_NUMBER_INLINE(x));
}
-// ECMA 262 - 15.8.2.18
-function MathTan(x) {
- return MathSin(x) / MathCos(x);
-}
-
// Non-standard extension.
function MathImul(x, y) {
return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
}
+// -------------------------------------------------------------------
+
+// A straightforward translation of fdlibm routines for sin, cos, and
+// tan, by Raymond Toy (rtoy@google.com).
+
+// ====================================================
+// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+//
+// Developed at SunSoft, a Sun Microsystems, Inc. business.
+// Permission to use, copy, modify, and distribute this
+// software is freely granted, provided that this notice
+// is preserved.
+// ====================================================
+
+var invpio2 = 6.36619772367581382433e-01;
+var pio2_1 = 1.57079632673412561417e+00;
+var pio2_1t = 6.07710050650619224932e-11;
+var pio2_2 = 6.07710050630396597660e-11;
+var pio2_2t = 2.02226624879595063154e-21;
+
+// Table of values of multiples of pi/2 from pi/2 to 50*pi/2. This is
+// used as a quick check to see if an argument is close to a multiple
+// of pi/2 and needs extra bits for reduction. This array contains
+// the high word the multiple of pi/2.
+
+var npio2_hw = [
+ 0x3FF921FB, 0x400921FB, 0x4012D97C, 0x401921FB, 0x401F6A7A, 0x4022D97C,
+ 0x4025FDBB, 0x402921FB, 0x402C463A, 0x402F6A7A, 0x4031475C, 0x4032D97C,
+ 0x40346B9C, 0x4035FDBB, 0x40378FDB, 0x403921FB, 0x403AB41B, 0x403C463A,
+ 0x403DD85A, 0x403F6A7A, 0x40407E4C, 0x4041475C, 0x4042106C, 0x4042D97C,
+ 0x4043A28C, 0x40446B9C, 0x404534AC, 0x4045FDBB, 0x4046C6CB, 0x40478FDB,
+ 0x404858EB, 0x404921FB
+];
+
+macro REMPIO2(x)
+ var n, y0, y1;
+ var hx = %_DoubleHi(x);
+ var ix = hx & 0x7fffffff;
+
+ if (ix < 0x4002d97c) {
+ // |x| ~< 3*pi/4, special case with n = +/- 1
+ if (hx > 0) {
+ var z = x - pio2_1;
+ if (ix != 0x3ff921fb) {
+ // 33+53 bit pi is good enough
+ y0 = z - pio2_1t;
+ y1 = (z - y0) - pio2_1t;
+ } else {
+ // near pi/2, use 33+33+53 bit pi
+ z -= pio2_2;
+ y0 = z - pio2_2t;
+ y1 = (z - y0) - pio2_2t;
+ }
+ n = 1;
+ } else {
+ // Negative x
+ var z = x + pio2_1;
+ if (ix != 0x3ff921fb) {
+ // 33+53 bit pi is good enough
+ y0 = z + pio2_1t;
+ y1 = (z - y0) + pio2_1t;
+ } else {
+ // near pi/2, use 33+33+53 bit pi
+ z += pio2_2;
+ y0 = z + pio2_2t;
+ y1 = (z - y0) + pio2_2t;
+ }
+ n = -1;
+ }
+ } else if (ix <= 0x413921fb) {
+ // |x| ~<= 2^19*(pi/2), medium size
+ var t = MathAbs(x);
+ n = MathRound(t * invpio2);
+ var fn = n;
+ var r = t - fn * pio2_1;
+ var w = fn * pio2_1t;
+ // First round good to 85 bit
+ if (n < 32 && ix != npio2_hw[n - 1]) {
+ // Quick check for cancellation
+ y0 = r - w;
+ } else {
+ var j = ix >> 20;
+ y0 = r - w;
+ var i = j - (( %_DoubleHi(y0) >> 20) & 0x7ff);
+ if (i > 16) {
+ // 2nd iteration needed, good to 118
+ t = r;
+ w = fn * pio2_2;
+ r = t - w;
+ w = fn * pio2_2t - ((t - r) - w);
+ y0 = r - w;
+ }
Raymond Toy 2014/06/02 17:26:11 As you mentioned via email, you've removed the 3rd
Yang 2014/06/03 07:01:45 That's true. However, the reduction step is not ex
+ }
+ y1 = (r - y0) - w;
+ if (hx < 0) {
+ n = -n;
+ y0 = -y0;
+ y1 = -y1;
+ }
+ } else if (ix >= 0x7ff00000) {
Raymond Toy 2014/06/09 21:28:38 Why is this case removed?
Yang 2014/06/10 06:51:54 This case wasn't removed. It was moved to C++. Mot
+ n = 0;
+ y0 = NAN;
+ y1 = NAN;
+ } else {
+ // Need to do full Payne-Hanek reduction here!
+ var r = %RemPiO2(x);
+ n = r[0];
+ y0 = r[1];
+ y1 = r[2];
+ }
+endmacro
+
+// Sine for [-pi/4, pi/4], pi/4 ~ 0.7854
+
+var S1 = -1.66666666666666324348e-01;
+var S2 = 8.33333333332248946124e-03;
+var S3 = -1.98412698298579493134e-04;
+var S4 = 2.75573137070700676789e-06;
+var S5 = -2.50507602534068634195e-08;
+var S6 = 1.58969099521155010221e-10;
+
+function KernelSin(x, y) {
+ var z = x * x;
+ var v = z * x;
+ var r = S2 + z * (S3 + z * (S4 + z * (S5 + z * S6)));
+ return x - ((z * (0.5 * y - v * r) - y) - v * S1);
+}
+
+// Cosine for [-pi/4, pi/4], pi/4 ~ 0.7854
+
+var C1 = 4.16666666666666019037e-02;
+var C2 = -1.38888888888741095749e-03;
+var C3 = 2.48015872894767294178e-05;
+var C4 = -2.75573143513906633035e-07;
+var C5 = 2.08757232129817482790e-09;
+var C6 = -1.13596475577881948265e-11;
+
+function KernelCos(x, y) {
+ var ix = %_DoubleHi(x) & 0x7fffffff;
+ var z = x * x;
+ var r = z * (C1 + z * (C2 + z * (C3 + z * (C4 + z * (C5 + z * C6)))));
+ if (ix < 0x3fd33333) {
+ return 1 - (0.5 * z - (z * r - x * y));
+ } else {
+ var qx;
+ if (ix > 0x3fe90000) {
+ qx = 0.28125;
+ } else {
+ qx = %_ConstructDouble(%_DoubleHi(0.25 * x), 0);
+ }
+ var hz = 0.5 * z - qx;
+ var a = 1 - qx;
+ return a - (hz - (z * r - x * y));
+ }
+}
+
+// Tangent for [-pi/4, pi/4], pi/4 ~ 0.7854
+
+var T0 = 3.33333333333334091986e-01;
+var T1 = 1.33333333333201242699e-01;
+var T2 = 5.39682539762260521377e-02;
+var T3 = 2.18694882948595424599e-02;
+var T4 = 8.86323982359930005737e-03;
+var T5 = 3.59207910759131235356e-03;
+var T6 = 1.45620945432529025516e-03;
+var T7 = 5.88041240820264096874e-04;
+var T8 = 2.46463134818469906812e-04;
+var T9 = 7.81794442939557092300e-05;
+var T10 = 7.14072491382608190305e-05;
+var T11 = -1.85586374855275456654e-05;
+var T12 = 2.59073051863633712884e-05;
+
+function KernelTan(x, y, returnTan) {
+ var z;
+ var w;
+ var hx = %_DoubleHi(x);
+ var ix = hx & 0x7fffffff;
+
+ if (ix < 0x3e300000) {
+ // x < 2^-28
+ // We don't try to generate inexact.
+ if (((ix | %_DoubleLo(x)) | (returnTan + 1)) == 0) {
+ return 1 / Math.abs(x);
+ } else {
+ if (returnTan == 1) {
+ return x;
+ } else {
+ // Compute -1/(x + y) carefully
+ var w = x + y;
+ var z = %_ConstructDouble( %_DoubleHi(w), 0);
+ var v = y - (z - x);
+ var a = -1 / w;
+ var t = %_ConstructDouble( %_DoubleHi(a), 0);
+ var s = 1 + t * z;
+ return t + a * (s + t * v);
+ }
+ }
+ }
+ if (ix >= 0x3fe59429) {
+ // |x| > .6744
+ if (x < 0) {
+ x = -x;
+ y = -y;
+ }
+ var pio4 = 7.85398163397448278999e-01;
+ var pio4lo = 3.06161699786838301793e-17;
+ z = pio4 - x;
+ w = pio4lo - y;
+ x = z + w;
+ y = 0;
+ }
+ z = x * x;
+ w = z * z;
+ //
+ // Break x^5*(T[1]+x^2*T[2]+...) into
+ // x^5(T[1]+x^4*T[3]+...+x^20*T[11]) +
+ // x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12]))
+ //
+ var r = T1 + w * (T3 + w * (T5 + w * (T7 + w * (T9 + w * T11))));
+ var v = z * (T2 + w * (T4 + w * (T6 + w * (T8 + w * (T10 + w * T12)))));
+ var s = z * x;
+ r = y + z * (s * (r + v) + y);
+ r = r + T0 * s;
+ w = x + r;
+ if (ix >= 0x3fe59428) {
+ return (1 - ((hx >> 30) & 2)) *
+ (returnTan - 2.0 * (x - (w * w / (w + returnTan) - r)));
+ }
+ if (returnTan == 1) {
+ return w;
+ } else {
+ // Compute -1/(x+r) accurately
+ z = %_ConstructDouble( %_DoubleHi(w), 0);
+ v = r - (z - x); // z+v = r+x
+ var a = -1 / w;
+ var t = %_ConstructDouble( %_DoubleHi(a), 0);
+ s = 1 + t * z;
+ return t + a * (s + t * v);
+ }
+}
+
-var kInversePiHalf = 0.636619772367581343; // 2 / pi
-var kInversePiHalfS26 = 9.48637384723993156e-9; // 2 / pi / (2^26)
-var kS26 = 1 << 26;
-var kTwoStepThreshold = 1 << 27;
-// pi / 2 rounded up
-var kPiHalf = 1.570796326794896780; // 0x192d4454fb21f93f
-// We use two parts for pi/2 to emulate a higher precision.
-// pi_half_1 only has 26 significant bits for mantissa.
-// Note that pi_half > pi_half_1 + pi_half_2
-var kPiHalf1 = 1.570796325802803040; // 0x00000054fb21f93f
-var kPiHalf2 = 9.920935796805404252e-10; // 0x3326a611460b113e
-
-var kSamples; // Initialized to a number during genesis.
-var kIndexConvert; // Initialized to kSamples / (pi/2) during genesis.
-var kSinTable; // Initialized to a Float64Array during genesis.
-var kCosXIntervalTable; // Initialized to a Float64Array during genesis.
-
-// This implements sine using the following algorithm.
-// 1) Multiplication takes care of to-number conversion.
-// 2) Reduce x to the first quadrant [0, pi/2].
-// Conveniently enough, in case of +/-Infinity, we get NaN.
-// Note that we try to use only 26 instead of 52 significant bits for
-// mantissa to avoid rounding errors when multiplying. For very large
-// input we therefore have additional steps.
-// 3) Replace x by (pi/2-x) if x was in the 2nd or 4th quadrant.
-// 4) Do a table lookup for the closest samples to the left and right of x.
-// 5) Find the derivatives at those sampling points by table lookup:
-// dsin(x)/dx = cos(x) = sin(pi/2-x) for x in [0, pi/2].
-// 6) Use cubic spline interpolation to approximate sin(x).
-// 7) Negate the result if x was in the 3rd or 4th quadrant.
-// 8) Get rid of -0 by adding 0.
-function TrigonometricInterpolation(x, phase) {
- if (x < 0 || x > kPiHalf) {
- var multiple;
- while (x < -kTwoStepThreshold || x > kTwoStepThreshold) {
- // Let's assume this loop does not terminate.
- // All numbers x in each loop forms a set S.
- // (1) abs(x) > 2^27 for all x in S.
- // (2) abs(multiple) != 0 since (2^27 * inverse_pi_half_s26) > 1
- // (3) multiple is rounded down in 2^26 steps, so the rounding error is
- // at most max(ulp, 2^26).
- // (4) so for x > 2^27, we subtract at most (1+pi/4)x and at least
- // (1-pi/4)x
- // (5) The subtraction results in x' so that abs(x') <= abs(x)*pi/4.
- // Note that this difference cannot be simply rounded off.
- // Set S cannot exist since (5) violates (1). Loop must terminate.
- multiple = MathFloor(x * kInversePiHalfS26) * kS26;
- x = x - multiple * kPiHalf1 - multiple * kPiHalf2;
+function MathSinSlow(x) {
+ REMPIO2(x);
+ if (n & 2) {
+ if (n & 1) {
+ return -KernelCos(y0, y1);
+ } else {
+ return -KernelSin(y0, y1);
}
- multiple = MathFloor(x * kInversePiHalf);
- x = x - multiple * kPiHalf1 - multiple * kPiHalf2;
- phase += multiple;
+ } else {
+ if (n & 1) {
+ return KernelCos(y0, y1);
+ } else {
+ return KernelSin(y0, y1);
+ }
+ }
+}
+
+function MathCosSlow(x) {
+ REMPIO2(x);
+ if (n & 2) {
+ if (n & 1) {
+ return KernelSin(y0, y1);
+ } else {
+ return -KernelCos(y0, y1);
+ }
+ } else {
+ if (n & 1) {
+ return -KernelSin(y0, y1);
+ } else {
+ return KernelCos(y0, y1);
+ }
+ }
+}
+
+function MathTanSlow(x) {
+ REMPIO2(x);
+ // flag is 1 if n is even and -1 if n is odd
+ var flag = (n & 1) ? -1 : 1;
+ return KernelTan(y0, y1, flag)
+}
+
+//ECMA 262 - 15.8.2.16
+function MathSin(x) {
+ x = x * 1; // Convert to number and deal with -0.
+ if (%_IsMinusZero(x)) return x;
+ if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) {
+ // |x| < pi/4, approximately. No reduction needed.
+ var z = x * x;
+ var v = z * x;
+ var r = S2 + z * (S3 + z * (S4 + z * (S5 + z * S6)));
+ return x + v * (S1 + z * r);
+ }
+ return MathSinSlow(x);
+}
+
+//ECMA 262 - 15.8.2.7
+function MathCos(x) {
+ x = x * 1; // Convert to number;
+ if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) {
+ // |x| < pi/4, approximately. No reduction needed.
+ return KernelCos(x, 0);
+ }
+ return MathCosSlow(x);
+}
+
+//ECMA 262 - 15.8.2.18
+function MathTan(x) {
+ x = x * 1; // Convert to number and deal with -0.
+ if (%_IsMinusZero(x)) return x;
+ if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) {
+ // |x| < pi/4, approximately. No reduction needed.
+ return KernelTan(x, 0, 1);
}
- var double_index = x * kIndexConvert;
- if (phase & 1) double_index = kSamples - double_index;
- var index = double_index | 0;
- var t1 = double_index - index;
- var t2 = 1 - t1;
- var y1 = kSinTable[index];
- var y2 = kSinTable[index + 1];
- var dy = y2 - y1;
- return (t2 * y1 + t1 * y2 +
- t1 * t2 * ((kCosXIntervalTable[index] - dy) * t2 +
- (dy - kCosXIntervalTable[index + 1]) * t1))
- * (1 - (phase & 2)) + 0;
+ return MathTanSlow(x);
}
// -------------------------------------------------------------------
@@ -308,7 +540,9 @@ function SetUpMath() {
%SetInlineBuiltinFlag(MathSin);
%SetInlineBuiltinFlag(MathCos);
%SetInlineBuiltinFlag(MathTan);
- %SetInlineBuiltinFlag(TrigonometricInterpolation);
+ %SetInlineBuiltinFlag(KernelSin);
+ %SetInlineBuiltinFlag(KernelCos);
+ %SetInlineBuiltinFlag(KernelTan);
}
SetUpMath();
« no previous file with comments | « src/bootstrapper.cc ('k') | src/rempio2.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698