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.8
     60 function MathExp(x) {
     61   return %MathExpRT(TO_NUMBER_INLINE(x));
     62 }
     63 
     64 // ECMA 262 - 15.8.2.9
     65 function MathFloor(x) {
     66   x = TO_NUMBER_INLINE(x);
     67   // It's more common to call this with a positive number that's out
     68   // of range than negative numbers; check the upper bound first.
     69   if (x < 0x80000000 && x > 0) {
     70     // Numbers in the range [0, 2^31) can be floored by converting
     71     // them to an unsigned 32-bit value using the shift operator.
     72     // We avoid doing so for -0, because the result of Math.floor(-0)
     73     // has to be -0, which wouldn't be the case with the shift.
     74     return TO_UINT32(x);
     75   } else {
     76     return %MathFloorRT(x);
     77   }
     78 }
     79 
     80 // ECMA 262 - 15.8.2.10
     81 function MathLog(x) {
     82   return %_MathLogRT(TO_NUMBER_INLINE(x));
     83 }
     84 
     85 // ECMA 262 - 15.8.2.11
     86 function MathMax(arg1, arg2) {  // length == 2
     87   var length = %_ArgumentsLength();
     88   if (length == 2) {
     89     arg1 = TO_NUMBER_INLINE(arg1);
     90     arg2 = TO_NUMBER_INLINE(arg2);
     91     if (arg2 > arg1) return arg2;
     92     if (arg1 > arg2) return arg1;
     93     if (arg1 == arg2) {
     94       // Make sure -0 is considered less than +0.
     95       return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
     96     }
     97     // All comparisons failed, one of the arguments must be NaN.
     98     return NAN;
     99   }
    100   var r = -INFINITY;
    101   for (var i = 0; i < length; i++) {
    102     var n = %_Arguments(i);
    103     if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
    104     // Make sure +0 is considered greater than -0.
    105     if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) {
    106       r = n;
    107     }
    108   }
    109   return r;
    110 }
    111 
    112 // ECMA 262 - 15.8.2.12
    113 function MathMin(arg1, arg2) {  // length == 2
    114   var length = %_ArgumentsLength();
    115   if (length == 2) {
    116     arg1 = TO_NUMBER_INLINE(arg1);
    117     arg2 = TO_NUMBER_INLINE(arg2);
    118     if (arg2 > arg1) return arg1;
    119     if (arg1 > arg2) return arg2;
    120     if (arg1 == arg2) {
    121       // Make sure -0 is considered less than +0.
    122       return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg1 : arg2;
    123     }
    124     // All comparisons failed, one of the arguments must be NaN.
    125     return NAN;
    126   }
    127   var r = INFINITY;
    128   for (var i = 0; i < length; i++) {
    129     var n = %_Arguments(i);
    130     if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
    131     // Make sure -0 is considered less than +0.
    132     if (NUMBER_IS_NAN(n) || n < r || (r === 0 && n === 0 && %_IsMinusZero(n))) {
    133       r = n;
    134     }
    135   }
    136   return r;
    137 }
    138 
    139 // ECMA 262 - 15.8.2.13
    140 function MathPow(x, y) {
    141   return %_MathPow(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
    142 }
    143 
    144 // ECMA 262 - 15.8.2.14
    145 var rngstate;  // Initialized to a Uint32Array during genesis.
    146 function MathRandom() {
    147   var r0 = (MathImul(18273, rngstate[0] & 0xFFFF) + (rngstate[0] >>> 16)) | 0;
    148   rngstate[0] = r0;
    149   var r1 = (MathImul(36969, rngstate[1] & 0xFFFF) + (rngstate[1] >>> 16)) | 0;
    150   rngstate[1] = r1;
    151   var x = ((r0 << 16) + (r1 & 0xFFFF)) | 0;
    152   // Division by 0x100000000 through multiplication by reciprocal.
    153   return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10;
    154 }
    155 
    156 // ECMA 262 - 15.8.2.15
    157 function MathRound(x) {
    158   return %RoundNumber(TO_NUMBER_INLINE(x));
    159 }
    160 
    161 // ECMA 262 - 15.8.2.17
    162 function MathSqrt(x) {
    163   return %_MathSqrtRT(TO_NUMBER_INLINE(x));
    164 }
    165 
    166 // Non-standard extension.
    167 function MathImul(x, y) {
    168   return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
    169 }
    170 
    171 // ES6 draft 09-27-13, section 20.2.2.28.
    172 function MathSign(x) {
    173   x = TO_NUMBER_INLINE(x);
    174   if (x > 0) return 1;
    175   if (x < 0) return -1;
    176   // -0, 0 or NaN.
    177   return x;
    178 }
    179 
    180 // ES6 draft 09-27-13, section 20.2.2.34.
    181 function MathTrunc(x) {
    182   x = TO_NUMBER_INLINE(x);
    183   if (x > 0) return MathFloor(x);
    184   if (x < 0) return MathCeil(x);
    185   // -0, 0 or NaN.
    186   return x;
    187 }
    188 
    189 // ES6 draft 09-27-13, section 20.2.2.33.
    190 function MathTanh(x) {
    191   if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
    192   // Idempotent for +/-0.
    193   if (x === 0) return x;
    194   // Returns +/-1 for +/-Infinity.
    195   if (!NUMBER_IS_FINITE(x)) return MathSign(x);
    196   var exp1 = MathExp(x);
    197   var exp2 = MathExp(-x);
    198   return (exp1 - exp2) / (exp1 + exp2);
    199 }
    200 
    201 // ES6 draft 09-27-13, section 20.2.2.5.
    202 function MathAsinh(x) {
    203   if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
    204   // Idempotent for NaN, +/-0 and +/-Infinity.
    205   if (x === 0 || !NUMBER_IS_FINITE(x)) return x;
    206   if (x > 0) return MathLog(x + MathSqrt(x * x + 1));
    207   // This is to prevent numerical errors caused by large negative x.
    208   return -MathLog(-x + MathSqrt(x * x + 1));
    209 }
    210 
    211 // ES6 draft 09-27-13, section 20.2.2.3.
    212 function MathAcosh(x) {
    213   if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
    214   if (x < 1) return NAN;
    215   // Idempotent for NaN and +Infinity.
    216   if (!NUMBER_IS_FINITE(x)) return x;
    217   return MathLog(x + MathSqrt(x + 1) * MathSqrt(x - 1));
    218 }
    219 
    220 // ES6 draft 09-27-13, section 20.2.2.7.
    221 function MathAtanh(x) {
    222   if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
    223   // Idempotent for +/-0.
    224   if (x === 0) return x;
    225   // Returns NaN for NaN and +/- Infinity.
    226   if (!NUMBER_IS_FINITE(x)) return NAN;
    227   return 0.5 * MathLog((1 + x) / (1 - x));
    228 }
    229 
    230 // ES6 draft 09-27-13, section 20.2.2.21.
    231 function MathLog10(x) {
    232   return MathLog(x) * 0.434294481903251828;  // log10(x) = log(x)/log(10).
    233 }
    234 
    235 
    236 // ES6 draft 09-27-13, section 20.2.2.22.
    237 function MathLog2(x) {
    238   return MathLog(x) * 1.442695040888963407;  // log2(x) = log(x)/log(2).
    239 }
    240 
    241 // ES6 draft 09-27-13, section 20.2.2.17.
    242 function MathHypot(x, y) {  // Function length is 2.
    243   // We may want to introduce fast paths for two arguments and when
    244   // normalization to avoid overflow is not necessary.  For now, we
    245   // simply assume the general case.
    246   var length = %_ArgumentsLength();
    247   var args = new InternalArray(length);
    248   var max = 0;
    249   for (var i = 0; i < length; i++) {
    250     var n = %_Arguments(i);
    251     if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
    252     if (n === INFINITY || n === -INFINITY) return INFINITY;
    253     n = MathAbs(n);
    254     if (n > max) max = n;
    255     args[i] = n;
    256   }
    257 
    258   // Kahan summation to avoid rounding errors.
    259   // Normalize the numbers to the largest one to avoid overflow.
    260   if (max === 0) max = 1;
    261   var sum = 0;
    262   var compensation = 0;
    263   for (var i = 0; i < length; i++) {
    264     var n = args[i] / max;
    265     var summand = n * n - compensation;
    266     var preliminary = sum + summand;
    267     compensation = (preliminary - sum) - summand;
    268     sum = preliminary;
    269   }
    270   return MathSqrt(sum) * max;
    271 }
    272 
    273 // ES6 draft 09-27-13, section 20.2.2.16.
    274 function MathFroundJS(x) {
    275   return %MathFround(TO_NUMBER_INLINE(x));
    276 }
    277 
    278 // ES6 draft 07-18-14, section 20.2.2.11
    279 function MathClz32(x) {
    280   x = ToUint32(TO_NUMBER_INLINE(x));
    281   if (x == 0) return 32;
    282   var result = 0;
    283   // Binary search.
    284   if ((x & 0xFFFF0000) === 0) { x <<= 16; result += 16; };
    285   if ((x & 0xFF000000) === 0) { x <<=  8; result +=  8; };
    286   if ((x & 0xF0000000) === 0) { x <<=  4; result +=  4; };
    287   if ((x & 0xC0000000) === 0) { x <<=  2; result +=  2; };
    288   if ((x & 0x80000000) === 0) { x <<=  1; result +=  1; };
    289   return result;
    290 }
    291 
    292 // ES6 draft 09-27-13, section 20.2.2.9.
    293 // Cube root approximation, refer to: http://metamerist.com/cbrt/cbrt.htm
    294 // Using initial approximation adapted from Kahan's cbrt and 4 iterations
    295 // of Newton's method.
    296 function MathCbrt(x) {
    297   if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
    298   if (x == 0 || !NUMBER_IS_FINITE(x)) return x;
    299   return x >= 0 ? CubeRoot(x) : -CubeRoot(-x);
    300 }
    301 
    302 macro NEWTON_ITERATION_CBRT(x, approx)
    303   (1.0 / 3.0) * (x / (approx * approx) + 2 * approx);
    304 endmacro
    305 
    306 function CubeRoot(x) {
    307   var approx_hi = MathFloor(%_DoubleHi(x) / 3) + 0x2A9F7893;
    308   var approx = %_ConstructDouble(approx_hi, 0);
    309   approx = NEWTON_ITERATION_CBRT(x, approx);
    310   approx = NEWTON_ITERATION_CBRT(x, approx);
    311   approx = NEWTON_ITERATION_CBRT(x, approx);
    312   return NEWTON_ITERATION_CBRT(x, approx);
    313 }
    314 
    315 // -------------------------------------------------------------------
    316 
    317 function SetUpMath() {
    318   %CheckIsBootstrapping();
    319 
    320   %InternalSetPrototype($Math, $Object.prototype);
    321   %AddNamedProperty(global, "Math", $Math, DONT_ENUM);
    322   %FunctionSetInstanceClassName(MathConstructor, 'Math');
    323 
    324   // Set up math constants.
    325   InstallConstants($Math, $Array(
    326     // ECMA-262, section 15.8.1.1.
    327     "E", 2.7182818284590452354,
    328     // ECMA-262, section 15.8.1.2.
    329     "LN10", 2.302585092994046,
    330     // ECMA-262, section 15.8.1.3.
    331     "LN2", 0.6931471805599453,
    332     // ECMA-262, section 15.8.1.4.
    333     "LOG2E", 1.4426950408889634,
    334     "LOG10E", 0.4342944819032518,
    335     "PI", 3.1415926535897932,
    336     "SQRT1_2", 0.7071067811865476,
    337     "SQRT2", 1.4142135623730951
    338   ));
    339 
    340   // Set up non-enumerable functions of the Math object and
    341   // set their names.
    342   InstallFunctions($Math, DONT_ENUM, $Array(
    343     "random", MathRandom,
    344     "abs", MathAbs,
    345     "acos", MathAcosJS,
    346     "asin", MathAsinJS,
    347     "atan", MathAtanJS,
    348     "ceil", MathCeil,
    349     "cos", MathCos,       // implemented by third_party/fdlibm
    350     "exp", MathExp,
    351     "floor", MathFloor,
    352     "log", MathLog,
    353     "round", MathRound,
    354     "sin", MathSin,       // implemented by third_party/fdlibm
    355     "sqrt", MathSqrt,
    356     "tan", MathTan,       // implemented by third_party/fdlibm
    357     "atan2", MathAtan2JS,
    358     "pow", MathPow,
    359     "max", MathMax,
    360     "min", MathMin,
    361     "imul", MathImul,
    362     "sign", MathSign,
    363     "trunc", MathTrunc,
    364     "sinh", MathSinh,     // implemented by third_party/fdlibm
    365     "cosh", MathCosh,     // implemented by third_party/fdlibm
    366     "tanh", MathTanh,
    367     "asinh", MathAsinh,
    368     "acosh", MathAcosh,
    369     "atanh", MathAtanh,
    370     "log10", MathLog10,
    371     "log2", MathLog2,
    372     "hypot", MathHypot,
    373     "fround", MathFroundJS,
    374     "clz32", MathClz32,
    375     "cbrt", MathCbrt,
    376     "log1p", MathLog1p,   // implemented by third_party/fdlibm
    377     "expm1", MathExpm1    // implemented by third_party/fdlibm
    378   ));
    379 
    380   %SetInlineBuiltinFlag(MathCeil);
    381   %SetInlineBuiltinFlag(MathRandom);
    382   %SetInlineBuiltinFlag(MathSin);
    383   %SetInlineBuiltinFlag(MathCos);
    384 }
    385 
    386 SetUpMath();
    387