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.8 60function MathExp(x) { 61 return %MathExpRT(TO_NUMBER_INLINE(x)); 62} 63 64// ECMA 262 - 15.8.2.9 65function 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 81function MathLog(x) { 82 return %_MathLogRT(TO_NUMBER_INLINE(x)); 83} 84 85// ECMA 262 - 15.8.2.11 86function 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 113function 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 140function MathPow(x, y) { 141 return %_MathPow(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y)); 142} 143 144// ECMA 262 - 15.8.2.14 145var rngstate; // Initialized to a Uint32Array during genesis. 146function 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 157function MathRound(x) { 158 return %RoundNumber(TO_NUMBER_INLINE(x)); 159} 160 161// ECMA 262 - 15.8.2.17 162function MathSqrt(x) { 163 return %_MathSqrtRT(TO_NUMBER_INLINE(x)); 164} 165 166// Non-standard extension. 167function 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. 172function 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. 181function 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. 190function 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. 202function 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. 212function 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. 221function 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. 231function 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. 237function 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. 242function 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. 274function MathFroundJS(x) { 275 return %MathFround(TO_NUMBER_INLINE(x)); 276} 277 278// ES6 draft 07-18-14, section 20.2.2.11 279function 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. 296function 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 302macro NEWTON_ITERATION_CBRT(x, approx) 303 (1.0 / 3.0) * (x / (approx * approx) + 2 * approx); 304endmacro 305 306function 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 317function 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 386SetUpMath(); 387