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. 14var $floor = MathFloor; 15var $abs = MathAbs; 16 17// Instance class name can only be set on functions. That is the only 18// purpose for MathConstructor. 19function MathConstructor() {} 20var $Math = new MathConstructor(); 21 22// ------------------------------------------------------------------- 23 24// ECMA 262 - 15.8.2.1 25function 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 33function MathAcosJS(x) { 34 return %MathAcos(TO_NUMBER_INLINE(x)); 35} 36 37// ECMA 262 - 15.8.2.3 38function MathAsinJS(x) { 39 return %MathAsin(TO_NUMBER_INLINE(x)); 40} 41 42// ECMA 262 - 15.8.2.4 43function 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. 50function MathAtan2JS(y, x) { 51 return %MathAtan2(TO_NUMBER_INLINE(y), TO_NUMBER_INLINE(x)); 52} 53 54// ECMA 262 - 15.8.2.6 55function MathCeil(x) { 56 return -MathFloor(-x); 57} 58 59// ECMA 262 - 15.8.2.7 60function 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 66function MathExp(x) { 67 return %MathExpRT(TO_NUMBER_INLINE(x)); 68} 69 70// ECMA 262 - 15.8.2.9 71function 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 87function MathLog(x) { 88 return %_MathLogRT(TO_NUMBER_INLINE(x)); 89} 90 91// ECMA 262 - 15.8.2.11 92function 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 119function 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 146function MathPow(x, y) { 147 return %_MathPow(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y)); 148} 149 150// ECMA 262 - 15.8.2.14 151var rngstate; // Initialized to a Uint32Array during genesis. 152function 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 163function MathRound(x) { 164 return %RoundNumber(TO_NUMBER_INLINE(x)); 165} 166 167// ECMA 262 - 15.8.2.16 168function 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 175function MathSqrt(x) { 176 return %_MathSqrtRT(TO_NUMBER_INLINE(x)); 177} 178 179// ECMA 262 - 15.8.2.18 180function MathTan(x) { 181 return MathSin(x) / MathCos(x); 182} 183 184// Non-standard extension. 185function MathImul(x, y) { 186 return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y)); 187} 188 189 190var kInversePiHalf = 0.636619772367581343; // 2 / pi 191var kInversePiHalfS26 = 9.48637384723993156e-9; // 2 / pi / (2^26) 192var kS26 = 1 << 26; 193var kTwoStepThreshold = 1 << 27; 194// pi / 2 rounded up 195var 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 199var kPiHalf1 = 1.570796325802803040; // 0x00000054fb21f93f 200var kPiHalf2 = 9.920935796805404252e-10; // 0x3326a611460b113e 201 202var kSamples; // Initialized to a number during genesis. 203var kIndexConvert; // Initialized to kSamples / (pi/2) during genesis. 204var kSinTable; // Initialized to a Float64Array during genesis. 205var 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. 221function 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 259function 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 314SetUpMath(); 315