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