blob: cc478d344895361648b0bdba55a7bc5a96822e8d [file] [log] [blame]
Ben Murdoch3ef787d2012-04-12 10:51:47 +01001// Copyright 2012 the V8 project authors. All rights reserved.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Steve Blocka7e24c12009-10-30 11:49:00 +00004
Ben Murdochb8a8cc12014-11-26 15:28:44 +00005"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;
Steve Blocka7e24c12009-10-30 11:49:00 +000010
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.
Ben Murdoch3ef787d2012-04-12 10:51:47 +010014var $floor = MathFloor;
Ben Murdoch3ef787d2012-04-12 10:51:47 +010015var $abs = MathAbs;
Steve Blocka7e24c12009-10-30 11:49:00 +000016
17// Instance class name can only be set on functions. That is the only
18// purpose for MathConstructor.
19function MathConstructor() {}
Ben Murdoch3ef787d2012-04-12 10:51:47 +010020var $Math = new MathConstructor();
Ben Murdochb8a8cc12014-11-26 15:28:44 +000021
22// -------------------------------------------------------------------
Steve Blocka7e24c12009-10-30 11:49:00 +000023
24// ECMA 262 - 15.8.2.1
25function MathAbs(x) {
26 if (%_IsSmi(x)) return x >= 0 ? x : -x;
Ben Murdochb8a8cc12014-11-26 15:28:44 +000027 x = TO_NUMBER_INLINE(x);
Steve Block6ded16b2010-05-10 14:33:55 +010028 if (x === 0) return 0; // To handle -0.
29 return x > 0 ? x : -x;
Steve Blocka7e24c12009-10-30 11:49:00 +000030}
31
32// ECMA 262 - 15.8.2.2
Ben Murdochb8a8cc12014-11-26 15:28:44 +000033function MathAcosJS(x) {
34 return %MathAcos(TO_NUMBER_INLINE(x));
Steve Blocka7e24c12009-10-30 11:49:00 +000035}
36
37// ECMA 262 - 15.8.2.3
Ben Murdochb8a8cc12014-11-26 15:28:44 +000038function MathAsinJS(x) {
39 return %MathAsin(TO_NUMBER_INLINE(x));
Steve Blocka7e24c12009-10-30 11:49:00 +000040}
41
42// ECMA 262 - 15.8.2.4
Ben Murdochb8a8cc12014-11-26 15:28:44 +000043function MathAtanJS(x) {
44 return %MathAtan(TO_NUMBER_INLINE(x));
Steve Blocka7e24c12009-10-30 11:49:00 +000045}
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.
Ben Murdochb8a8cc12014-11-26 15:28:44 +000050function MathAtan2JS(y, x) {
51 return %MathAtan2(TO_NUMBER_INLINE(y), TO_NUMBER_INLINE(x));
Steve Blocka7e24c12009-10-30 11:49:00 +000052}
53
54// ECMA 262 - 15.8.2.6
55function MathCeil(x) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000056 return -MathFloor(-x);
Steve Blocka7e24c12009-10-30 11:49:00 +000057}
58
59// ECMA 262 - 15.8.2.8
60function MathExp(x) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000061 return %MathExpRT(TO_NUMBER_INLINE(x));
Steve Blocka7e24c12009-10-30 11:49:00 +000062}
63
64// ECMA 262 - 15.8.2.9
65function MathFloor(x) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000066 x = TO_NUMBER_INLINE(x);
Steve Blocka7e24c12009-10-30 11:49:00 +000067 // 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.
Leon Clarkee46be812010-01-19 14:06:41 +000069 if (x < 0x80000000 && x > 0) {
Steve Blocka7e24c12009-10-30 11:49:00 +000070 // 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.
Leon Clarkee46be812010-01-19 14:06:41 +000074 return TO_UINT32(x);
Steve Blocka7e24c12009-10-30 11:49:00 +000075 } else {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000076 return %MathFloorRT(x);
Steve Blocka7e24c12009-10-30 11:49:00 +000077 }
78}
79
80// ECMA 262 - 15.8.2.10
81function MathLog(x) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000082 return %_MathLogRT(TO_NUMBER_INLINE(x));
Steve Blocka7e24c12009-10-30 11:49:00 +000083}
84
85// ECMA 262 - 15.8.2.11
86function MathMax(arg1, arg2) { // length == 2
Steve Blocka7e24c12009-10-30 11:49:00 +000087 var length = %_ArgumentsLength();
Ben Murdoch3ef787d2012-04-12 10:51:47 +010088 if (length == 2) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000089 arg1 = TO_NUMBER_INLINE(arg1);
90 arg2 = TO_NUMBER_INLINE(arg2);
Ben Murdoch3ef787d2012-04-12 10:51:47 +010091 if (arg2 > arg1) return arg2;
92 if (arg1 > arg2) return arg1;
93 if (arg1 == arg2) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000094 // Make sure -0 is considered less than +0.
95 return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
Ben Murdoch3ef787d2012-04-12 10:51:47 +010096 }
97 // All comparisons failed, one of the arguments must be NaN.
Ben Murdochb8a8cc12014-11-26 15:28:44 +000098 return NAN;
Ben Murdoch3ef787d2012-04-12 10:51:47 +010099 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000100 var r = -INFINITY;
101 for (var i = 0; i < length; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +0000102 var n = %_Arguments(i);
Ben Murdoch086aeea2011-05-13 15:57:08 +0100103 if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000104 // 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 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000108 }
109 return r;
110}
111
112// ECMA 262 - 15.8.2.12
113function MathMin(arg1, arg2) { // length == 2
Steve Blocka7e24c12009-10-30 11:49:00 +0000114 var length = %_ArgumentsLength();
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100115 if (length == 2) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000116 arg1 = TO_NUMBER_INLINE(arg1);
117 arg2 = TO_NUMBER_INLINE(arg2);
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100118 if (arg2 > arg1) return arg1;
119 if (arg1 > arg2) return arg2;
120 if (arg1 == arg2) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000121 // Make sure -0 is considered less than +0.
122 return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg1 : arg2;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100123 }
124 // All comparisons failed, one of the arguments must be NaN.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000125 return NAN;
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100126 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000127 var r = INFINITY;
128 for (var i = 0; i < length; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +0000129 var n = %_Arguments(i);
Ben Murdoch086aeea2011-05-13 15:57:08 +0100130 if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000131 // 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 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000135 }
136 return r;
137}
138
139// ECMA 262 - 15.8.2.13
140function MathPow(x, y) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000141 return %_MathPow(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
Steve Blocka7e24c12009-10-30 11:49:00 +0000142}
143
144// ECMA 262 - 15.8.2.14
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000145var rngstate; // Initialized to a Uint32Array during genesis.
Steve Blocka7e24c12009-10-30 11:49:00 +0000146function MathRandom() {
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400147 var r0 = (MathImul(18030, rngstate[0] & 0xFFFF) + (rngstate[0] >>> 16)) | 0;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000148 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;
Steve Blocka7e24c12009-10-30 11:49:00 +0000154}
155
156// ECMA 262 - 15.8.2.15
157function MathRound(x) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000158 return %RoundNumber(TO_NUMBER_INLINE(x));
Steve Blocka7e24c12009-10-30 11:49:00 +0000159}
160
161// ECMA 262 - 15.8.2.17
162function MathSqrt(x) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000163 return %_MathSqrtRT(TO_NUMBER_INLINE(x));
Steve Blocka7e24c12009-10-30 11:49:00 +0000164}
165
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000166// Non-standard extension.
167function MathImul(x, y) {
168 return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
Steve Blocka7e24c12009-10-30 11:49:00 +0000169}
170
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000171// 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
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000230// ES6 draft 09-27-13, section 20.2.2.17.
231function MathHypot(x, y) { // Function length is 2.
232 // We may want to introduce fast paths for two arguments and when
233 // normalization to avoid overflow is not necessary. For now, we
234 // simply assume the general case.
235 var length = %_ArgumentsLength();
236 var args = new InternalArray(length);
237 var max = 0;
238 for (var i = 0; i < length; i++) {
239 var n = %_Arguments(i);
240 if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
241 if (n === INFINITY || n === -INFINITY) return INFINITY;
242 n = MathAbs(n);
243 if (n > max) max = n;
244 args[i] = n;
245 }
246
247 // Kahan summation to avoid rounding errors.
248 // Normalize the numbers to the largest one to avoid overflow.
249 if (max === 0) max = 1;
250 var sum = 0;
251 var compensation = 0;
252 for (var i = 0; i < length; i++) {
253 var n = args[i] / max;
254 var summand = n * n - compensation;
255 var preliminary = sum + summand;
256 compensation = (preliminary - sum) - summand;
257 sum = preliminary;
258 }
259 return MathSqrt(sum) * max;
260}
261
262// ES6 draft 09-27-13, section 20.2.2.16.
263function MathFroundJS(x) {
264 return %MathFround(TO_NUMBER_INLINE(x));
265}
266
267// ES6 draft 07-18-14, section 20.2.2.11
268function MathClz32(x) {
269 x = ToUint32(TO_NUMBER_INLINE(x));
270 if (x == 0) return 32;
271 var result = 0;
272 // Binary search.
273 if ((x & 0xFFFF0000) === 0) { x <<= 16; result += 16; };
274 if ((x & 0xFF000000) === 0) { x <<= 8; result += 8; };
275 if ((x & 0xF0000000) === 0) { x <<= 4; result += 4; };
276 if ((x & 0xC0000000) === 0) { x <<= 2; result += 2; };
277 if ((x & 0x80000000) === 0) { x <<= 1; result += 1; };
278 return result;
279}
280
281// ES6 draft 09-27-13, section 20.2.2.9.
282// Cube root approximation, refer to: http://metamerist.com/cbrt/cbrt.htm
283// Using initial approximation adapted from Kahan's cbrt and 4 iterations
284// of Newton's method.
285function MathCbrt(x) {
286 if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
287 if (x == 0 || !NUMBER_IS_FINITE(x)) return x;
288 return x >= 0 ? CubeRoot(x) : -CubeRoot(-x);
289}
290
291macro NEWTON_ITERATION_CBRT(x, approx)
292 (1.0 / 3.0) * (x / (approx * approx) + 2 * approx);
293endmacro
294
295function CubeRoot(x) {
296 var approx_hi = MathFloor(%_DoubleHi(x) / 3) + 0x2A9F7893;
297 var approx = %_ConstructDouble(approx_hi, 0);
298 approx = NEWTON_ITERATION_CBRT(x, approx);
299 approx = NEWTON_ITERATION_CBRT(x, approx);
300 approx = NEWTON_ITERATION_CBRT(x, approx);
301 return NEWTON_ITERATION_CBRT(x, approx);
302}
Steve Blocka7e24c12009-10-30 11:49:00 +0000303
304// -------------------------------------------------------------------
305
Ben Murdoch589d6972011-11-30 16:04:58 +0000306function SetUpMath() {
307 %CheckIsBootstrapping();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000308
309 %InternalSetPrototype($Math, $Object.prototype);
310 %AddNamedProperty(global, "Math", $Math, DONT_ENUM);
311 %FunctionSetInstanceClassName(MathConstructor, 'Math');
312
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400313 %AddNamedProperty($Math, symbolToStringTag, "Math", READ_ONLY | DONT_ENUM);
314
Ben Murdoch589d6972011-11-30 16:04:58 +0000315 // Set up math constants.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000316 InstallConstants($Math, $Array(
317 // ECMA-262, section 15.8.1.1.
318 "E", 2.7182818284590452354,
319 // ECMA-262, section 15.8.1.2.
320 "LN10", 2.302585092994046,
321 // ECMA-262, section 15.8.1.3.
322 "LN2", 0.6931471805599453,
323 // ECMA-262, section 15.8.1.4.
324 "LOG2E", 1.4426950408889634,
325 "LOG10E", 0.4342944819032518,
326 "PI", 3.1415926535897932,
327 "SQRT1_2", 0.7071067811865476,
328 "SQRT2", 1.4142135623730951
329 ));
Steve Blocka7e24c12009-10-30 11:49:00 +0000330
Ben Murdoch589d6972011-11-30 16:04:58 +0000331 // Set up non-enumerable functions of the Math object and
Steve Blocka7e24c12009-10-30 11:49:00 +0000332 // set their names.
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100333 InstallFunctions($Math, DONT_ENUM, $Array(
Steve Blocka7e24c12009-10-30 11:49:00 +0000334 "random", MathRandom,
335 "abs", MathAbs,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000336 "acos", MathAcosJS,
337 "asin", MathAsinJS,
338 "atan", MathAtanJS,
Steve Blocka7e24c12009-10-30 11:49:00 +0000339 "ceil", MathCeil,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000340 "cos", MathCos, // implemented by third_party/fdlibm
Steve Blocka7e24c12009-10-30 11:49:00 +0000341 "exp", MathExp,
342 "floor", MathFloor,
343 "log", MathLog,
344 "round", MathRound,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000345 "sin", MathSin, // implemented by third_party/fdlibm
Steve Blocka7e24c12009-10-30 11:49:00 +0000346 "sqrt", MathSqrt,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000347 "tan", MathTan, // implemented by third_party/fdlibm
348 "atan2", MathAtan2JS,
Steve Blocka7e24c12009-10-30 11:49:00 +0000349 "pow", MathPow,
350 "max", MathMax,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000351 "min", MathMin,
352 "imul", MathImul,
353 "sign", MathSign,
354 "trunc", MathTrunc,
355 "sinh", MathSinh, // implemented by third_party/fdlibm
356 "cosh", MathCosh, // implemented by third_party/fdlibm
357 "tanh", MathTanh,
358 "asinh", MathAsinh,
359 "acosh", MathAcosh,
360 "atanh", MathAtanh,
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400361 "log10", MathLog10, // implemented by third_party/fdlibm
362 "log2", MathLog2, // implemented by third_party/fdlibm
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000363 "hypot", MathHypot,
364 "fround", MathFroundJS,
365 "clz32", MathClz32,
366 "cbrt", MathCbrt,
367 "log1p", MathLog1p, // implemented by third_party/fdlibm
368 "expm1", MathExpm1 // implemented by third_party/fdlibm
Steve Blocka7e24c12009-10-30 11:49:00 +0000369 ));
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000370
371 %SetInlineBuiltinFlag(MathCeil);
372 %SetInlineBuiltinFlag(MathRandom);
373 %SetInlineBuiltinFlag(MathSin);
374 %SetInlineBuiltinFlag(MathCos);
Ben Murdoch589d6972011-11-30 16:04:58 +0000375}
Steve Blocka7e24c12009-10-30 11:49:00 +0000376
Ben Murdoch589d6972011-11-30 16:04:58 +0000377SetUpMath();