| Index: src/math.js
|
| diff --git a/src/math.js b/src/math.js
|
| index 9dc4b37d0ce2115ed9e9b5078f204fd060f926b9..6323451d1b94dc5c3ec59922640da024eacc44a5 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,251 @@ 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 kTrig; // Initialized to a Float64Array during genesis.
|
| +
|
| +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 - kTrig[1];
|
| + if (ix != 0x3ff921fb) {
|
| + // 33+53 bit pi is good enough
|
| + y0 = z - kTrig[2];
|
| + y1 = (z - y0) - kTrig[2];
|
| + } else {
|
| + // near pi/2, use 33+33+53 bit pi
|
| + z -= kTrig[3];
|
| + y0 = z - kTrig[4];
|
| + y1 = (z - y0) - kTrig[4];
|
| + }
|
| + n = 1;
|
| + } else {
|
| + // Negative X
|
| + var z = X + kTrig[1];
|
| + if (ix != 0x3ff921fb) {
|
| + // 33+53 bit pi is good enough
|
| + y0 = z + kTrig[2];
|
| + y1 = (z - y0) + kTrig[2];
|
| + } else {
|
| + // near pi/2, use 33+33+53 bit pi
|
| + z += kTrig[3];
|
| + y0 = z + kTrig[4];
|
| + y1 = (z - y0) + kTrig[4];
|
| + }
|
| + n = -1;
|
| + }
|
| + } else if (ix <= 0x413921fb) {
|
| + // |X| ~<= 2^19*(pi/2), medium size
|
| + var t = MathAbs(X);
|
| + n = (t * kTrig[0] + 0.5) | 0;
|
| + var r = t - n * kTrig[1];
|
| + var w = n * kTrig[2];
|
| + // First round good to 85 bit
|
| + y0 = r - w;
|
| + if (ix - (%_DoubleHi(y0) & 0x7ff00000) > 0x1000000) {
|
| + // 2nd iteration needed, good to 118
|
| + t = r;
|
| + w = n * kTrig[3];
|
| + r = t - w;
|
| + w = n * kTrig[4] - ((t - r) - w);
|
| + y0 = r - w;
|
| + if (ix - (%_DoubleHi(y0) & 0x7ff00000) > 0x3100000) {
|
| + // 3rd iteration needed. 151 bits accuracy
|
| + t = r;
|
| + w = n * kTrig[5];
|
| + r = t - w;
|
| + w = n * kTrig[6] - ((t - r) - w);
|
| + y0 = r - w;
|
| + }
|
| + }
|
| + y1 = (r - y0) - w;
|
| + if (hx < 0) {
|
| + n = -n;
|
| + y0 = -y0;
|
| + y1 = -y1;
|
| + }
|
| + } 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
|
| +macro RETURN_KERNELSIN(X0, X1, SIGN)
|
| + var z = X0 * X0;
|
| + var v = z * X0;
|
| + var r = kTrig[8] + z * (kTrig[9] + z * (kTrig[10] +
|
| + z * (kTrig[11] + z * kTrig[12])));
|
| + return (X0 - ((z * (0.5 * X1 - v * r) - X1) - v * kTrig[7])) SIGN;
|
| +endmacro
|
|
|
| -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;
|
| +// Cosine for [-pi/4, pi/4], pi/4 ~ 0.7854
|
| +macro RETURN_KERNELCOS(X0, X1, SIGN)
|
| + var ix = %_DoubleHi(X0) & 0x7fffffff;
|
| + var z = X0 * X0;
|
| + var r = z * (kTrig[13] + z * (kTrig[14] + z * (kTrig[15] +
|
| + z * (kTrig[16] + z * (kTrig[17] + z * kTrig[18])))));
|
| + if (ix < 0x3fd33333) {
|
| + return (1 - (0.5 * z - (z * r - X0 * X1))) SIGN;
|
| + } else {
|
| + var qx;
|
| + if (ix > 0x3fe90000) {
|
| + qx = 0.28125;
|
| + } else {
|
| + qx = %_ConstructDouble(%_DoubleHi(0.25 * X0), 0);
|
| }
|
| - multiple = MathFloor(x * kInversePiHalf);
|
| - x = x - multiple * kPiHalf1 - multiple * kPiHalf2;
|
| - phase += multiple;
|
| + var hz = 0.5 * z - qx;
|
| + return (1 - qx - (hz - (z * r - X0 * X1))) SIGN;
|
| + }
|
| +endmacro
|
| +
|
| +// Tangent for [-pi/4, pi/4], pi/4 ~ 0.7854
|
| +function KernelTan(x, y, returnTan) {
|
| + var z;
|
| + var w;
|
| + var hx = %_DoubleHi(x);
|
| + var ix = hx & 0x7fffffff;
|
| +
|
| + if (ix < 0x3e300000) {
|
| + // x < 2^-28
|
| + if (((ix | %_DoubleLo(x)) | (returnTan + 1)) == 0) {
|
| + return 1 / MathAbs(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;
|
| + }
|
| + z = kTrig[32] - x;
|
| + w = kTrig[33] - y;
|
| + x = z + w;
|
| + y = 0;
|
| + }
|
| + z = x * x;
|
| + w = z * z;
|
| +
|
| + var r = kTrig[20] + w * (kTrig[22] + w * (kTrig[24] +
|
| + w * (kTrig[26] + w * (kTrig[28] + w * kTrig[30]))));
|
| + var v = z * (kTrig[21] + w * (kTrig[23] + w * (kTrig[25] +
|
| + w * (kTrig[27] + w * (kTrig[29] + w * kTrig[31])))));
|
| + var s = z * x;
|
| + r = y + z * (s * (r + v) + y);
|
| + r = r + kTrig[19] * 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 {
|
| + z = %_ConstructDouble(%_DoubleHi(w), 0);
|
| + v = r - (z - x);
|
| + var a = -1 / w;
|
| + var t = %_ConstructDouble(%_DoubleHi(a), 0);
|
| + s = 1 + t * z;
|
| + return t + a * (s + t * v);
|
| + }
|
| +}
|
| +
|
| +function MathSinSlow(x) {
|
| + REMPIO2(x);
|
| + var sign = 1 - (n & 2);
|
| + if (n & 1) {
|
| + RETURN_KERNELCOS(y0, y1, * sign);
|
| + } else {
|
| + RETURN_KERNELSIN(y0, y1, * sign);
|
| + }
|
| +}
|
| +
|
| +function MathCosSlow(x) {
|
| + REMPIO2(x);
|
| + if (n & 1) {
|
| + var sign = (n & 2) - 1;
|
| + RETURN_KERNELSIN(y0, y1, * sign);
|
| + } else {
|
| + var sign = 1 - (n & 2);
|
| + RETURN_KERNELCOS(y0, y1, * sign);
|
| + }
|
| +}
|
| +
|
| +// ECMA 262 - 15.8.2.16
|
| +function MathSin(x) {
|
| + x = x * 1; // Convert to number.
|
| + if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) {
|
| + // |x| < pi/4, approximately. No reduction needed.
|
| + if (%_IsMinusZero(x)) return x;
|
| + RETURN_KERNELSIN(x, 0, /* empty */);
|
| + }
|
| + 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, /* empty */);
|
| + }
|
| + return MathCosSlow(x);
|
| +}
|
| +
|
| +// ECMA 262 - 15.8.2.18
|
| +function MathTan(x) {
|
| + x = x * 1; // Convert to number.
|
| + if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) {
|
| + // |x| < pi/4, approximately. No reduction needed.
|
| + if (%_IsMinusZero(x)) return x;
|
| + 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;
|
| + REMPIO2(x);
|
| + return KernelTan(y0, y1, (n & 1) ? -1 : 1);
|
| }
|
|
|
|
|
| @@ -537,8 +688,6 @@ function SetUpMath() {
|
| %SetInlineBuiltinFlag(MathRandom);
|
| %SetInlineBuiltinFlag(MathSin);
|
| %SetInlineBuiltinFlag(MathCos);
|
| - %SetInlineBuiltinFlag(MathTan);
|
| - %SetInlineBuiltinFlag(TrigonometricInterpolation);
|
| }
|
|
|
| SetUpMath();
|
|
|