• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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