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