Home | History | Annotate | Download | only in src
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 // This file relies on the fact that the following declarations have been made
     29 // in runtime.js:
     30 // var $Object = global.Object;
     31 
     32 // Keep reference to original values of some global properties.  This
     33 // has the added benefit that the code in this file is isolated from
     34 // changes to these properties.
     35 var $floor = MathFloor;
     36 var $abs = MathAbs;
     37 
     38 // Instance class name can only be set on functions. That is the only
     39 // purpose for MathConstructor.
     40 function MathConstructor() {}
     41 var $Math = new MathConstructor();
     42 
     43 // -------------------------------------------------------------------
     44 
     45 // ECMA 262 - 15.8.2.1
     46 function MathAbs(x) {
     47   if (%_IsSmi(x)) return x >= 0 ? x : -x;
     48   x = TO_NUMBER_INLINE(x);
     49   if (x === 0) return 0;  // To handle -0.
     50   return x > 0 ? x : -x;
     51 }
     52 
     53 // ECMA 262 - 15.8.2.2
     54 function MathAcos(x) {
     55   return %Math_acos(TO_NUMBER_INLINE(x));
     56 }
     57 
     58 // ECMA 262 - 15.8.2.3
     59 function MathAsin(x) {
     60   return %Math_asin(TO_NUMBER_INLINE(x));
     61 }
     62 
     63 // ECMA 262 - 15.8.2.4
     64 function MathAtan(x) {
     65   return %Math_atan(TO_NUMBER_INLINE(x));
     66 }
     67 
     68 // ECMA 262 - 15.8.2.5
     69 // The naming of y and x matches the spec, as does the order in which
     70 // ToNumber (valueOf) is called.
     71 function MathAtan2(y, x) {
     72   return %Math_atan2(TO_NUMBER_INLINE(y), TO_NUMBER_INLINE(x));
     73 }
     74 
     75 // ECMA 262 - 15.8.2.6
     76 function MathCeil(x) {
     77   return -MathFloor(-x);
     78 }
     79 
     80 // ECMA 262 - 15.8.2.7
     81 function MathCos(x) {
     82   x = MathAbs(x);  // Convert to number and get rid of -0.
     83   return TrigonometricInterpolation(x, 1);
     84 }
     85 
     86 // ECMA 262 - 15.8.2.8
     87 function MathExp(x) {
     88   return %Math_exp(TO_NUMBER_INLINE(x));
     89 }
     90 
     91 // ECMA 262 - 15.8.2.9
     92 function MathFloor(x) {
     93   x = TO_NUMBER_INLINE(x);
     94   // It's more common to call this with a positive number that's out
     95   // of range than negative numbers; check the upper bound first.
     96   if (x < 0x80000000 && x > 0) {
     97     // Numbers in the range [0, 2^31) can be floored by converting
     98     // them to an unsigned 32-bit value using the shift operator.
     99     // We avoid doing so for -0, because the result of Math.floor(-0)
    100     // has to be -0, which wouldn't be the case with the shift.
    101     return TO_UINT32(x);
    102   } else {
    103     return %Math_floor(x);
    104   }
    105 }
    106 
    107 // ECMA 262 - 15.8.2.10
    108 function MathLog(x) {
    109   return %_MathLog(TO_NUMBER_INLINE(x));
    110 }
    111 
    112 // ECMA 262 - 15.8.2.11
    113 function MathMax(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 arg2;
    119     if (arg1 > arg2) return arg1;
    120     if (arg1 == arg2) {
    121       // Make sure -0 is considered less than +0.
    122       return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
    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 greater than -0.
    132     if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) {
    133       r = n;
    134     }
    135   }
    136   return r;
    137 }
    138 
    139 // ECMA 262 - 15.8.2.12
    140 function MathMin(arg1, arg2) {  // length == 2
    141   var length = %_ArgumentsLength();
    142   if (length == 2) {
    143     arg1 = TO_NUMBER_INLINE(arg1);
    144     arg2 = TO_NUMBER_INLINE(arg2);
    145     if (arg2 > arg1) return arg1;
    146     if (arg1 > arg2) return arg2;
    147     if (arg1 == arg2) {
    148       // Make sure -0 is considered less than +0.
    149       return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg1 : arg2;
    150     }
    151     // All comparisons failed, one of the arguments must be NaN.
    152     return NAN;
    153   }
    154   var r = INFINITY;
    155   for (var i = 0; i < length; i++) {
    156     var n = %_Arguments(i);
    157     if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
    158     // Make sure -0 is considered less than +0.
    159     if (NUMBER_IS_NAN(n) || n < r || (r === 0 && n === 0 && %_IsMinusZero(n))) {
    160       r = n;
    161     }
    162   }
    163   return r;
    164 }
    165 
    166 // ECMA 262 - 15.8.2.13
    167 function MathPow(x, y) {
    168   return %_MathPow(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
    169 }
    170 
    171 // ECMA 262 - 15.8.2.14
    172 var rngstate;  // Initialized to a Uint32Array during genesis.
    173 function MathRandom() {
    174   var r0 = (MathImul(18273, rngstate[0] & 0xFFFF) + (rngstate[0] >>> 16)) | 0;
    175   rngstate[0] = r0;
    176   var r1 = (MathImul(36969, rngstate[1] & 0xFFFF) + (rngstate[1] >>> 16)) | 0;
    177   rngstate[1] = r1;
    178   var x = ((r0 << 16) + (r1 & 0xFFFF)) | 0;
    179   // Division by 0x100000000 through multiplication by reciprocal.
    180   return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10;
    181 }
    182 
    183 // ECMA 262 - 15.8.2.15
    184 function MathRound(x) {
    185   return %RoundNumber(TO_NUMBER_INLINE(x));
    186 }
    187 
    188 // ECMA 262 - 15.8.2.16
    189 function MathSin(x) {
    190   x = x * 1;  // Convert to number and deal with -0.
    191   if (%_IsMinusZero(x)) return x;
    192   return TrigonometricInterpolation(x, 0);
    193 }
    194 
    195 // ECMA 262 - 15.8.2.17
    196 function MathSqrt(x) {
    197   return %_MathSqrt(TO_NUMBER_INLINE(x));
    198 }
    199 
    200 // ECMA 262 - 15.8.2.18
    201 function MathTan(x) {
    202   return MathSin(x) / MathCos(x);
    203 }
    204 
    205 // Non-standard extension.
    206 function MathImul(x, y) {
    207   return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
    208 }
    209 
    210 
    211 var kInversePiHalf      = 0.636619772367581343;      // 2 / pi
    212 var kInversePiHalfS26   = 9.48637384723993156e-9;    // 2 / pi / (2^26)
    213 var kS26                = 1 << 26;
    214 var kTwoStepThreshold   = 1 << 27;
    215 // pi / 2 rounded up
    216 var kPiHalf             = 1.570796326794896780;      // 0x192d4454fb21f93f
    217 // We use two parts for pi/2 to emulate a higher precision.
    218 // pi_half_1 only has 26 significant bits for mantissa.
    219 // Note that pi_half > pi_half_1 + pi_half_2
    220 var kPiHalf1            = 1.570796325802803040;      // 0x00000054fb21f93f
    221 var kPiHalf2            = 9.920935796805404252e-10;  // 0x3326a611460b113e
    222 
    223 var kSamples;            // Initialized to a number during genesis.
    224 var kIndexConvert;       // Initialized to kSamples / (pi/2) during genesis.
    225 var kSinTable;           // Initialized to a Float64Array during genesis.
    226 var kCosXIntervalTable;  // Initialized to a Float64Array during genesis.
    227 
    228 // This implements sine using the following algorithm.
    229 // 1) Multiplication takes care of to-number conversion.
    230 // 2) Reduce x to the first quadrant [0, pi/2].
    231 //    Conveniently enough, in case of +/-Infinity, we get NaN.
    232 //    Note that we try to use only 26 instead of 52 significant bits for
    233 //    mantissa to avoid rounding errors when multiplying.  For very large
    234 //    input we therefore have additional steps.
    235 // 3) Replace x by (pi/2-x) if x was in the 2nd or 4th quadrant.
    236 // 4) Do a table lookup for the closest samples to the left and right of x.
    237 // 5) Find the derivatives at those sampling points by table lookup:
    238 //    dsin(x)/dx = cos(x) = sin(pi/2-x) for x in [0, pi/2].
    239 // 6) Use cubic spline interpolation to approximate sin(x).
    240 // 7) Negate the result if x was in the 3rd or 4th quadrant.
    241 // 8) Get rid of -0 by adding 0.
    242 function TrigonometricInterpolation(x, phase) {
    243   if (x < 0 || x > kPiHalf) {
    244     var multiple;
    245     while (x < -kTwoStepThreshold || x > kTwoStepThreshold) {
    246       // Let's assume this loop does not terminate.
    247       // All numbers x in each loop forms a set S.
    248       // (1) abs(x) > 2^27 for all x in S.
    249       // (2) abs(multiple) != 0 since (2^27 * inverse_pi_half_s26) > 1
    250       // (3) multiple is rounded down in 2^26 steps, so the rounding error is
    251       //     at most max(ulp, 2^26).
    252       // (4) so for x > 2^27, we subtract at most (1+pi/4)x and at least
    253       //     (1-pi/4)x
    254       // (5) The subtraction results in x' so that abs(x') <= abs(x)*pi/4.
    255       //     Note that this difference cannot be simply rounded off.
    256       // Set S cannot exist since (5) violates (1).  Loop must terminate.
    257       multiple = MathFloor(x * kInversePiHalfS26) * kS26;
    258       x = x - multiple * kPiHalf1 - multiple * kPiHalf2;
    259     }
    260     multiple = MathFloor(x * kInversePiHalf);
    261     x = x - multiple * kPiHalf1 - multiple * kPiHalf2;
    262     phase += multiple;
    263   }
    264   var double_index = x * kIndexConvert;
    265   if (phase & 1) double_index = kSamples - double_index;
    266   var index = double_index | 0;
    267   var t1 = double_index - index;
    268   var t2 = 1 - t1;
    269   var y1 = kSinTable[index];
    270   var y2 = kSinTable[index + 1];
    271   var dy = y2 - y1;
    272   return (t2 * y1 + t1 * y2 +
    273               t1 * t2 * ((kCosXIntervalTable[index] - dy) * t2 +
    274                          (dy - kCosXIntervalTable[index + 1]) * t1))
    275          * (1 - (phase & 2)) + 0;
    276 }
    277 
    278 // -------------------------------------------------------------------
    279 
    280 function SetUpMath() {
    281   %CheckIsBootstrapping();
    282 
    283   %SetPrototype($Math, $Object.prototype);
    284   %SetProperty(global, "Math", $Math, DONT_ENUM);
    285   %FunctionSetInstanceClassName(MathConstructor, 'Math');
    286 
    287   // Set up math constants.
    288   // ECMA-262, section 15.8.1.1.
    289   %OptimizeObjectForAddingMultipleProperties($Math, 8);
    290   %SetProperty($Math,
    291                "E",
    292                2.7182818284590452354,
    293                DONT_ENUM |  DONT_DELETE | READ_ONLY);
    294   // ECMA-262, section 15.8.1.2.
    295   %SetProperty($Math,
    296                "LN10",
    297                2.302585092994046,
    298                DONT_ENUM |  DONT_DELETE | READ_ONLY);
    299   // ECMA-262, section 15.8.1.3.
    300   %SetProperty($Math,
    301                "LN2",
    302                0.6931471805599453,
    303                DONT_ENUM |  DONT_DELETE | READ_ONLY);
    304   // ECMA-262, section 15.8.1.4.
    305   %SetProperty($Math,
    306                "LOG2E",
    307                1.4426950408889634,
    308                DONT_ENUM |  DONT_DELETE | READ_ONLY);
    309   %SetProperty($Math,
    310                "LOG10E",
    311                0.4342944819032518,
    312                DONT_ENUM |  DONT_DELETE | READ_ONLY);
    313   %SetProperty($Math,
    314                "PI",
    315                3.1415926535897932,
    316                DONT_ENUM |  DONT_DELETE | READ_ONLY);
    317   %SetProperty($Math,
    318                "SQRT1_2",
    319                0.7071067811865476,
    320                DONT_ENUM |  DONT_DELETE | READ_ONLY);
    321   %SetProperty($Math,
    322                "SQRT2",
    323                1.4142135623730951,
    324                DONT_ENUM |  DONT_DELETE | READ_ONLY);
    325   %ToFastProperties($Math);
    326 
    327   // Set up non-enumerable functions of the Math object and
    328   // set their names.
    329   InstallFunctions($Math, DONT_ENUM, $Array(
    330     "random", MathRandom,
    331     "abs", MathAbs,
    332     "acos", MathAcos,
    333     "asin", MathAsin,
    334     "atan", MathAtan,
    335     "ceil", MathCeil,
    336     "cos", MathCos,
    337     "exp", MathExp,
    338     "floor", MathFloor,
    339     "log", MathLog,
    340     "round", MathRound,
    341     "sin", MathSin,
    342     "sqrt", MathSqrt,
    343     "tan", MathTan,
    344     "atan2", MathAtan2,
    345     "pow", MathPow,
    346     "max", MathMax,
    347     "min", MathMin,
    348     "imul", MathImul
    349   ));
    350 
    351   %SetInlineBuiltinFlag(MathCeil);
    352   %SetInlineBuiltinFlag(MathRandom);
    353   %SetInlineBuiltinFlag(MathSin);
    354   %SetInlineBuiltinFlag(MathCos);
    355   %SetInlineBuiltinFlag(MathTan);
    356   %SetInlineBuiltinFlag(TrigonometricInterpolation);
    357 }
    358 
    359 SetUpMath();
    360