Home | History | Annotate | Download | only in src
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 "use strict";
      6 
      7 // This file relies on the fact that the following declarations have been made
      8 // in runtime.js:
      9 // var $Object = global.Object;
     10 
     11 // Keep reference to original values of some global properties.  This
     12 // has the added benefit that the code in this file is isolated from
     13 // changes to these properties.
     14 var $floor = MathFloor;
     15 var $abs = MathAbs;
     16 
     17 // Instance class name can only be set on functions. That is the only
     18 // purpose for MathConstructor.
     19 function MathConstructor() {}
     20 var $Math = new MathConstructor();
     21 
     22 // -------------------------------------------------------------------
     23 
     24 // ECMA 262 - 15.8.2.1
     25 function MathAbs(x) {
     26   if (%_IsSmi(x)) return x >= 0 ? x : -x;
     27   x = TO_NUMBER_INLINE(x);
     28   if (x === 0) return 0;  // To handle -0.
     29   return x > 0 ? x : -x;
     30 }
     31 
     32 // ECMA 262 - 15.8.2.2
     33 function MathAcosJS(x) {
     34   return %MathAcos(TO_NUMBER_INLINE(x));
     35 }
     36 
     37 // ECMA 262 - 15.8.2.3
     38 function MathAsinJS(x) {
     39   return %MathAsin(TO_NUMBER_INLINE(x));
     40 }
     41 
     42 // ECMA 262 - 15.8.2.4
     43 function MathAtanJS(x) {
     44   return %MathAtan(TO_NUMBER_INLINE(x));
     45 }
     46 
     47 // ECMA 262 - 15.8.2.5
     48 // The naming of y and x matches the spec, as does the order in which
     49 // ToNumber (valueOf) is called.
     50 function MathAtan2JS(y, x) {
     51   return %MathAtan2(TO_NUMBER_INLINE(y), TO_NUMBER_INLINE(x));
     52 }
     53 
     54 // ECMA 262 - 15.8.2.6
     55 function MathCeil(x) {
     56   return -MathFloor(-x);
     57 }
     58 
     59 // ECMA 262 - 15.8.2.7
     60 function MathCos(x) {
     61   x = MathAbs(x);  // Convert to number and get rid of -0.
     62   return TrigonometricInterpolation(x, 1);
     63 }
     64 
     65 // ECMA 262 - 15.8.2.8
     66 function MathExp(x) {
     67   return %MathExpRT(TO_NUMBER_INLINE(x));
     68 }
     69 
     70 // ECMA 262 - 15.8.2.9
     71 function MathFloor(x) {
     72   x = TO_NUMBER_INLINE(x);
     73   // It's more common to call this with a positive number that's out
     74   // of range than negative numbers; check the upper bound first.
     75   if (x < 0x80000000 && x > 0) {
     76     // Numbers in the range [0, 2^31) can be floored by converting
     77     // them to an unsigned 32-bit value using the shift operator.
     78     // We avoid doing so for -0, because the result of Math.floor(-0)
     79     // has to be -0, which wouldn't be the case with the shift.
     80     return TO_UINT32(x);
     81   } else {
     82     return %MathFloorRT(x);
     83   }
     84 }
     85 
     86 // ECMA 262 - 15.8.2.10
     87 function MathLog(x) {
     88   return %_MathLogRT(TO_NUMBER_INLINE(x));
     89 }
     90 
     91 // ECMA 262 - 15.8.2.11
     92 function MathMax(arg1, arg2) {  // length == 2
     93   var length = %_ArgumentsLength();
     94   if (length == 2) {
     95     arg1 = TO_NUMBER_INLINE(arg1);
     96     arg2 = TO_NUMBER_INLINE(arg2);
     97     if (arg2 > arg1) return arg2;
     98     if (arg1 > arg2) return arg1;
     99     if (arg1 == arg2) {
    100       // Make sure -0 is considered less than +0.
    101       return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
    102     }
    103     // All comparisons failed, one of the arguments must be NaN.
    104     return NAN;
    105   }
    106   var r = -INFINITY;
    107   for (var i = 0; i < length; i++) {
    108     var n = %_Arguments(i);
    109     if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
    110     // Make sure +0 is considered greater than -0.
    111     if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) {
    112       r = n;
    113     }
    114   }
    115   return r;
    116 }
    117 
    118 // ECMA 262 - 15.8.2.12
    119 function MathMin(arg1, arg2) {  // length == 2
    120   var length = %_ArgumentsLength();
    121   if (length == 2) {
    122     arg1 = TO_NUMBER_INLINE(arg1);
    123     arg2 = TO_NUMBER_INLINE(arg2);
    124     if (arg2 > arg1) return arg1;
    125     if (arg1 > arg2) return arg2;
    126     if (arg1 == arg2) {
    127       // Make sure -0 is considered less than +0.
    128       return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg1 : arg2;
    129     }
    130     // All comparisons failed, one of the arguments must be NaN.
    131     return NAN;
    132   }
    133   var r = INFINITY;
    134   for (var i = 0; i < length; i++) {
    135     var n = %_Arguments(i);
    136     if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
    137     // Make sure -0 is considered less than +0.
    138     if (NUMBER_IS_NAN(n) || n < r || (r === 0 && n === 0 && %_IsMinusZero(n))) {
    139       r = n;
    140     }
    141   }
    142   return r;
    143 }
    144 
    145 // ECMA 262 - 15.8.2.13
    146 function MathPow(x, y) {
    147   return %_MathPow(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
    148 }
    149 
    150 // ECMA 262 - 15.8.2.14
    151 var rngstate;  // Initialized to a Uint32Array during genesis.
    152 function MathRandom() {
    153   var r0 = (MathImul(18273, rngstate[0] & 0xFFFF) + (rngstate[0] >>> 16)) | 0;
    154   rngstate[0] = r0;
    155   var r1 = (MathImul(36969, rngstate[1] & 0xFFFF) + (rngstate[1] >>> 16)) | 0;
    156   rngstate[1] = r1;
    157   var x = ((r0 << 16) + (r1 & 0xFFFF)) | 0;
    158   // Division by 0x100000000 through multiplication by reciprocal.
    159   return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10;
    160 }
    161 
    162 // ECMA 262 - 15.8.2.15
    163 function MathRound(x) {
    164   return %RoundNumber(TO_NUMBER_INLINE(x));
    165 }
    166 
    167 // ECMA 262 - 15.8.2.16
    168 function MathSin(x) {
    169   x = x * 1;  // Convert to number and deal with -0.
    170   if (%_IsMinusZero(x)) return x;
    171   return TrigonometricInterpolation(x, 0);
    172 }
    173 
    174 // ECMA 262 - 15.8.2.17
    175 function MathSqrt(x) {
    176   return %_MathSqrtRT(TO_NUMBER_INLINE(x));
    177 }
    178 
    179 // ECMA 262 - 15.8.2.18
    180 function MathTan(x) {
    181   return MathSin(x) / MathCos(x);
    182 }
    183 
    184 // Non-standard extension.
    185 function MathImul(x, y) {
    186   return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
    187 }
    188 
    189 
    190 var kInversePiHalf      = 0.636619772367581343;      // 2 / pi
    191 var kInversePiHalfS26   = 9.48637384723993156e-9;    // 2 / pi / (2^26)
    192 var kS26                = 1 << 26;
    193 var kTwoStepThreshold   = 1 << 27;
    194 // pi / 2 rounded up
    195 var kPiHalf             = 1.570796326794896780;      // 0x192d4454fb21f93f
    196 // We use two parts for pi/2 to emulate a higher precision.
    197 // pi_half_1 only has 26 significant bits for mantissa.
    198 // Note that pi_half > pi_half_1 + pi_half_2
    199 var kPiHalf1            = 1.570796325802803040;      // 0x00000054fb21f93f
    200 var kPiHalf2            = 9.920935796805404252e-10;  // 0x3326a611460b113e
    201 
    202 var kSamples;            // Initialized to a number during genesis.
    203 var kIndexConvert;       // Initialized to kSamples / (pi/2) during genesis.
    204 var kSinTable;           // Initialized to a Float64Array during genesis.
    205 var kCosXIntervalTable;  // Initialized to a Float64Array during genesis.
    206 
    207 // This implements sine using the following algorithm.
    208 // 1) Multiplication takes care of to-number conversion.
    209 // 2) Reduce x to the first quadrant [0, pi/2].
    210 //    Conveniently enough, in case of +/-Infinity, we get NaN.
    211 //    Note that we try to use only 26 instead of 52 significant bits for
    212 //    mantissa to avoid rounding errors when multiplying.  For very large
    213 //    input we therefore have additional steps.
    214 // 3) Replace x by (pi/2-x) if x was in the 2nd or 4th quadrant.
    215 // 4) Do a table lookup for the closest samples to the left and right of x.
    216 // 5) Find the derivatives at those sampling points by table lookup:
    217 //    dsin(x)/dx = cos(x) = sin(pi/2-x) for x in [0, pi/2].
    218 // 6) Use cubic spline interpolation to approximate sin(x).
    219 // 7) Negate the result if x was in the 3rd or 4th quadrant.
    220 // 8) Get rid of -0 by adding 0.
    221 function TrigonometricInterpolation(x, phase) {
    222   if (x < 0 || x > kPiHalf) {
    223     var multiple;
    224     while (x < -kTwoStepThreshold || x > kTwoStepThreshold) {
    225       // Let's assume this loop does not terminate.
    226       // All numbers x in each loop forms a set S.
    227       // (1) abs(x) > 2^27 for all x in S.
    228       // (2) abs(multiple) != 0 since (2^27 * inverse_pi_half_s26) > 1
    229       // (3) multiple is rounded down in 2^26 steps, so the rounding error is
    230       //     at most max(ulp, 2^26).
    231       // (4) so for x > 2^27, we subtract at most (1+pi/4)x and at least
    232       //     (1-pi/4)x
    233       // (5) The subtraction results in x' so that abs(x') <= abs(x)*pi/4.
    234       //     Note that this difference cannot be simply rounded off.
    235       // Set S cannot exist since (5) violates (1).  Loop must terminate.
    236       multiple = MathFloor(x * kInversePiHalfS26) * kS26;
    237       x = x - multiple * kPiHalf1 - multiple * kPiHalf2;
    238     }
    239     multiple = MathFloor(x * kInversePiHalf);
    240     x = x - multiple * kPiHalf1 - multiple * kPiHalf2;
    241     phase += multiple;
    242   }
    243   var double_index = x * kIndexConvert;
    244   if (phase & 1) double_index = kSamples - double_index;
    245   var index = double_index | 0;
    246   var t1 = double_index - index;
    247   var t2 = 1 - t1;
    248   var y1 = kSinTable[index];
    249   var y2 = kSinTable[index + 1];
    250   var dy = y2 - y1;
    251   return (t2 * y1 + t1 * y2 +
    252               t1 * t2 * ((kCosXIntervalTable[index] - dy) * t2 +
    253                          (dy - kCosXIntervalTable[index + 1]) * t1))
    254          * (1 - (phase & 2)) + 0;
    255 }
    256 
    257 // -------------------------------------------------------------------
    258 
    259 function SetUpMath() {
    260   %CheckIsBootstrapping();
    261 
    262   %SetPrototype($Math, $Object.prototype);
    263   %SetProperty(global, "Math", $Math, DONT_ENUM);
    264   %FunctionSetInstanceClassName(MathConstructor, 'Math');
    265 
    266   // Set up math constants.
    267   InstallConstants($Math, $Array(
    268     // ECMA-262, section 15.8.1.1.
    269     "E", 2.7182818284590452354,
    270     // ECMA-262, section 15.8.1.2.
    271     "LN10", 2.302585092994046,
    272     // ECMA-262, section 15.8.1.3.
    273     "LN2", 0.6931471805599453,
    274     // ECMA-262, section 15.8.1.4.
    275     "LOG2E", 1.4426950408889634,
    276     "LOG10E", 0.4342944819032518,
    277     "PI", 3.1415926535897932,
    278     "SQRT1_2", 0.7071067811865476,
    279     "SQRT2", 1.4142135623730951
    280   ));
    281 
    282   // Set up non-enumerable functions of the Math object and
    283   // set their names.
    284   InstallFunctions($Math, DONT_ENUM, $Array(
    285     "random", MathRandom,
    286     "abs", MathAbs,
    287     "acos", MathAcosJS,
    288     "asin", MathAsinJS,
    289     "atan", MathAtanJS,
    290     "ceil", MathCeil,
    291     "cos", MathCos,
    292     "exp", MathExp,
    293     "floor", MathFloor,
    294     "log", MathLog,
    295     "round", MathRound,
    296     "sin", MathSin,
    297     "sqrt", MathSqrt,
    298     "tan", MathTan,
    299     "atan2", MathAtan2JS,
    300     "pow", MathPow,
    301     "max", MathMax,
    302     "min", MathMin,
    303     "imul", MathImul
    304   ));
    305 
    306   %SetInlineBuiltinFlag(MathCeil);
    307   %SetInlineBuiltinFlag(MathRandom);
    308   %SetInlineBuiltinFlag(MathSin);
    309   %SetInlineBuiltinFlag(MathCos);
    310   %SetInlineBuiltinFlag(MathTan);
    311   %SetInlineBuiltinFlag(TrigonometricInterpolation);
    312 }
    313 
    314 SetUpMath();
    315