Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Side by Side Diff: src/math.js

Issue 303753002: Trigonometric functions using fdlibm. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: test case Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/bootstrapper.cc ('k') | src/rempio2.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 "use strict"; 5 "use strict";
6 6
7 // This file relies on the fact that the following declarations have been made 7 // This file relies on the fact that the following declarations have been made
8 // in runtime.js: 8 // in runtime.js:
9 // var $Object = global.Object; 9 // var $Object = global.Object;
10 10
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
49 // ToNumber (valueOf) is called. 49 // ToNumber (valueOf) is called.
50 function MathAtan2JS(y, x) { 50 function MathAtan2JS(y, x) {
51 return %MathAtan2(TO_NUMBER_INLINE(y), TO_NUMBER_INLINE(x)); 51 return %MathAtan2(TO_NUMBER_INLINE(y), TO_NUMBER_INLINE(x));
52 } 52 }
53 53
54 // ECMA 262 - 15.8.2.6 54 // ECMA 262 - 15.8.2.6
55 function MathCeil(x) { 55 function MathCeil(x) {
56 return -MathFloor(-x); 56 return -MathFloor(-x);
57 } 57 }
58 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 59 // ECMA 262 - 15.8.2.8
66 function MathExp(x) { 60 function MathExp(x) {
67 return %MathExpRT(TO_NUMBER_INLINE(x)); 61 return %MathExpRT(TO_NUMBER_INLINE(x));
68 } 62 }
69 63
70 // ECMA 262 - 15.8.2.9 64 // ECMA 262 - 15.8.2.9
71 function MathFloor(x) { 65 function MathFloor(x) {
72 x = TO_NUMBER_INLINE(x); 66 x = TO_NUMBER_INLINE(x);
73 // It's more common to call this with a positive number that's out 67 // 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. 68 // of range than negative numbers; check the upper bound first.
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 var x = ((r0 << 16) + (r1 & 0xFFFF)) | 0; 151 var x = ((r0 << 16) + (r1 & 0xFFFF)) | 0;
158 // Division by 0x100000000 through multiplication by reciprocal. 152 // Division by 0x100000000 through multiplication by reciprocal.
159 return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10; 153 return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10;
160 } 154 }
161 155
162 // ECMA 262 - 15.8.2.15 156 // ECMA 262 - 15.8.2.15
163 function MathRound(x) { 157 function MathRound(x) {
164 return %RoundNumber(TO_NUMBER_INLINE(x)); 158 return %RoundNumber(TO_NUMBER_INLINE(x));
165 } 159 }
166 160
167 // ECMA 262 - 15.8.2.16 161 // ECMA 262 - 15.8.2.17
162 function MathSqrt(x) {
163 return %_MathSqrtRT(TO_NUMBER_INLINE(x));
164 }
165
166 // Non-standard extension.
167 function MathImul(x, y) {
168 return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
169 }
170
171 // -------------------------------------------------------------------
172
173 // A straightforward translation of fdlibm routines for sin, cos, and
174 // tan, by Raymond Toy (rtoy@google.com).
175
176 // ====================================================
177 // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
178 //
179 // Developed at SunSoft, a Sun Microsystems, Inc. business.
180 // Permission to use, copy, modify, and distribute this
181 // software is freely granted, provided that this notice
182 // is preserved.
183 // ====================================================
184
185 var invpio2 = 6.36619772367581382433e-01;
186 var pio2_1 = 1.57079632673412561417e+00;
187 var pio2_1t = 6.07710050650619224932e-11;
188 var pio2_2 = 6.07710050630396597660e-11;
189 var pio2_2t = 2.02226624879595063154e-21;
190
191 // Table of values of multiples of pi/2 from pi/2 to 50*pi/2. This is
192 // used as a quick check to see if an argument is close to a multiple
193 // of pi/2 and needs extra bits for reduction. This array contains
194 // the high word the multiple of pi/2.
195
196 var npio2_hw = [
197 0x3FF921FB, 0x400921FB, 0x4012D97C, 0x401921FB, 0x401F6A7A, 0x4022D97C,
198 0x4025FDBB, 0x402921FB, 0x402C463A, 0x402F6A7A, 0x4031475C, 0x4032D97C,
199 0x40346B9C, 0x4035FDBB, 0x40378FDB, 0x403921FB, 0x403AB41B, 0x403C463A,
200 0x403DD85A, 0x403F6A7A, 0x40407E4C, 0x4041475C, 0x4042106C, 0x4042D97C,
201 0x4043A28C, 0x40446B9C, 0x404534AC, 0x4045FDBB, 0x4046C6CB, 0x40478FDB,
202 0x404858EB, 0x404921FB
203 ];
204
205 macro REMPIO2(x)
206 var n, y0, y1;
207 var hx = %_DoubleHi(x);
208 var ix = hx & 0x7fffffff;
209
210 if (ix < 0x4002d97c) {
211 // |x| ~< 3*pi/4, special case with n = +/- 1
212 if (hx > 0) {
213 var z = x - pio2_1;
214 if (ix != 0x3ff921fb) {
215 // 33+53 bit pi is good enough
216 y0 = z - pio2_1t;
217 y1 = (z - y0) - pio2_1t;
218 } else {
219 // near pi/2, use 33+33+53 bit pi
220 z -= pio2_2;
221 y0 = z - pio2_2t;
222 y1 = (z - y0) - pio2_2t;
223 }
224 n = 1;
225 } else {
226 // Negative x
227 var z = x + pio2_1;
228 if (ix != 0x3ff921fb) {
229 // 33+53 bit pi is good enough
230 y0 = z + pio2_1t;
231 y1 = (z - y0) + pio2_1t;
232 } else {
233 // near pi/2, use 33+33+53 bit pi
234 z += pio2_2;
235 y0 = z + pio2_2t;
236 y1 = (z - y0) + pio2_2t;
237 }
238 n = -1;
239 }
240 } else if (ix <= 0x413921fb) {
241 // |x| ~<= 2^19*(pi/2), medium size
242 var t = MathAbs(x);
243 n = MathRound(t * invpio2);
244 var fn = n;
245 var r = t - fn * pio2_1;
246 var w = fn * pio2_1t;
247 // First round good to 85 bit
248 if (n < 32 && ix != npio2_hw[n - 1]) {
249 // Quick check for cancellation
250 y0 = r - w;
251 } else {
252 var j = ix >> 20;
253 y0 = r - w;
254 var i = j - (( %_DoubleHi(y0) >> 20) & 0x7ff);
255 if (i > 16) {
256 // 2nd iteration needed, good to 118
257 t = r;
258 w = fn * pio2_2;
259 r = t - w;
260 w = fn * pio2_2t - ((t - r) - w);
261 y0 = r - w;
262 }
Raymond Toy 2014/06/02 17:26:11 As you mentioned via email, you've removed the 3rd
Yang 2014/06/03 07:01:45 That's true. However, the reduction step is not ex
263 }
264 y1 = (r - y0) - w;
265 if (hx < 0) {
266 n = -n;
267 y0 = -y0;
268 y1 = -y1;
269 }
270 } else if (ix >= 0x7ff00000) {
Raymond Toy 2014/06/09 21:28:38 Why is this case removed?
Yang 2014/06/10 06:51:54 This case wasn't removed. It was moved to C++. Mot
271 n = 0;
272 y0 = NAN;
273 y1 = NAN;
274 } else {
275 // Need to do full Payne-Hanek reduction here!
276 var r = %RemPiO2(x);
277 n = r[0];
278 y0 = r[1];
279 y1 = r[2];
280 }
281 endmacro
282
283 // Sine for [-pi/4, pi/4], pi/4 ~ 0.7854
284
285 var S1 = -1.66666666666666324348e-01;
286 var S2 = 8.33333333332248946124e-03;
287 var S3 = -1.98412698298579493134e-04;
288 var S4 = 2.75573137070700676789e-06;
289 var S5 = -2.50507602534068634195e-08;
290 var S6 = 1.58969099521155010221e-10;
291
292 function KernelSin(x, y) {
293 var z = x * x;
294 var v = z * x;
295 var r = S2 + z * (S3 + z * (S4 + z * (S5 + z * S6)));
296 return x - ((z * (0.5 * y - v * r) - y) - v * S1);
297 }
298
299 // Cosine for [-pi/4, pi/4], pi/4 ~ 0.7854
300
301 var C1 = 4.16666666666666019037e-02;
302 var C2 = -1.38888888888741095749e-03;
303 var C3 = 2.48015872894767294178e-05;
304 var C4 = -2.75573143513906633035e-07;
305 var C5 = 2.08757232129817482790e-09;
306 var C6 = -1.13596475577881948265e-11;
307
308 function KernelCos(x, y) {
309 var ix = %_DoubleHi(x) & 0x7fffffff;
310 var z = x * x;
311 var r = z * (C1 + z * (C2 + z * (C3 + z * (C4 + z * (C5 + z * C6)))));
312 if (ix < 0x3fd33333) {
313 return 1 - (0.5 * z - (z * r - x * y));
314 } else {
315 var qx;
316 if (ix > 0x3fe90000) {
317 qx = 0.28125;
318 } else {
319 qx = %_ConstructDouble(%_DoubleHi(0.25 * x), 0);
320 }
321 var hz = 0.5 * z - qx;
322 var a = 1 - qx;
323 return a - (hz - (z * r - x * y));
324 }
325 }
326
327 // Tangent for [-pi/4, pi/4], pi/4 ~ 0.7854
328
329 var T0 = 3.33333333333334091986e-01;
330 var T1 = 1.33333333333201242699e-01;
331 var T2 = 5.39682539762260521377e-02;
332 var T3 = 2.18694882948595424599e-02;
333 var T4 = 8.86323982359930005737e-03;
334 var T5 = 3.59207910759131235356e-03;
335 var T6 = 1.45620945432529025516e-03;
336 var T7 = 5.88041240820264096874e-04;
337 var T8 = 2.46463134818469906812e-04;
338 var T9 = 7.81794442939557092300e-05;
339 var T10 = 7.14072491382608190305e-05;
340 var T11 = -1.85586374855275456654e-05;
341 var T12 = 2.59073051863633712884e-05;
342
343 function KernelTan(x, y, returnTan) {
344 var z;
345 var w;
346 var hx = %_DoubleHi(x);
347 var ix = hx & 0x7fffffff;
348
349 if (ix < 0x3e300000) {
350 // x < 2^-28
351 // We don't try to generate inexact.
352 if (((ix | %_DoubleLo(x)) | (returnTan + 1)) == 0) {
353 return 1 / Math.abs(x);
354 } else {
355 if (returnTan == 1) {
356 return x;
357 } else {
358 // Compute -1/(x + y) carefully
359 var w = x + y;
360 var z = %_ConstructDouble( %_DoubleHi(w), 0);
361 var v = y - (z - x);
362 var a = -1 / w;
363 var t = %_ConstructDouble( %_DoubleHi(a), 0);
364 var s = 1 + t * z;
365 return t + a * (s + t * v);
366 }
367 }
368 }
369 if (ix >= 0x3fe59429) {
370 // |x| > .6744
371 if (x < 0) {
372 x = -x;
373 y = -y;
374 }
375 var pio4 = 7.85398163397448278999e-01;
376 var pio4lo = 3.06161699786838301793e-17;
377 z = pio4 - x;
378 w = pio4lo - y;
379 x = z + w;
380 y = 0;
381 }
382 z = x * x;
383 w = z * z;
384 //
385 // Break x^5*(T[1]+x^2*T[2]+...) into
386 // x^5(T[1]+x^4*T[3]+...+x^20*T[11]) +
387 // x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12]))
388 //
389 var r = T1 + w * (T3 + w * (T5 + w * (T7 + w * (T9 + w * T11))));
390 var v = z * (T2 + w * (T4 + w * (T6 + w * (T8 + w * (T10 + w * T12)))));
391 var s = z * x;
392 r = y + z * (s * (r + v) + y);
393 r = r + T0 * s;
394 w = x + r;
395 if (ix >= 0x3fe59428) {
396 return (1 - ((hx >> 30) & 2)) *
397 (returnTan - 2.0 * (x - (w * w / (w + returnTan) - r)));
398 }
399 if (returnTan == 1) {
400 return w;
401 } else {
402 // Compute -1/(x+r) accurately
403 z = %_ConstructDouble( %_DoubleHi(w), 0);
404 v = r - (z - x); // z+v = r+x
405 var a = -1 / w;
406 var t = %_ConstructDouble( %_DoubleHi(a), 0);
407 s = 1 + t * z;
408 return t + a * (s + t * v);
409 }
410 }
411
412
413 function MathSinSlow(x) {
414 REMPIO2(x);
415 if (n & 2) {
416 if (n & 1) {
417 return -KernelCos(y0, y1);
418 } else {
419 return -KernelSin(y0, y1);
420 }
421 } else {
422 if (n & 1) {
423 return KernelCos(y0, y1);
424 } else {
425 return KernelSin(y0, y1);
426 }
427 }
428 }
429
430 function MathCosSlow(x) {
431 REMPIO2(x);
432 if (n & 2) {
433 if (n & 1) {
434 return KernelSin(y0, y1);
435 } else {
436 return -KernelCos(y0, y1);
437 }
438 } else {
439 if (n & 1) {
440 return -KernelSin(y0, y1);
441 } else {
442 return KernelCos(y0, y1);
443 }
444 }
445 }
446
447 function MathTanSlow(x) {
448 REMPIO2(x);
449 // flag is 1 if n is even and -1 if n is odd
450 var flag = (n & 1) ? -1 : 1;
451 return KernelTan(y0, y1, flag)
452 }
453
454 //ECMA 262 - 15.8.2.16
168 function MathSin(x) { 455 function MathSin(x) {
169 x = x * 1; // Convert to number and deal with -0. 456 x = x * 1; // Convert to number and deal with -0.
170 if (%_IsMinusZero(x)) return x; 457 if (%_IsMinusZero(x)) return x;
171 return TrigonometricInterpolation(x, 0); 458 if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) {
172 } 459 // |x| < pi/4, approximately. No reduction needed.
173 460 var z = x * x;
174 // ECMA 262 - 15.8.2.17 461 var v = z * x;
175 function MathSqrt(x) { 462 var r = S2 + z * (S3 + z * (S4 + z * (S5 + z * S6)));
176 return %_MathSqrtRT(TO_NUMBER_INLINE(x)); 463 return x + v * (S1 + z * r);
177 } 464 }
178 465 return MathSinSlow(x);
179 // ECMA 262 - 15.8.2.18 466 }
467
468 //ECMA 262 - 15.8.2.7
469 function MathCos(x) {
470 x = x * 1; // Convert to number;
471 if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) {
472 // |x| < pi/4, approximately. No reduction needed.
473 return KernelCos(x, 0);
474 }
475 return MathCosSlow(x);
476 }
477
478 //ECMA 262 - 15.8.2.18
180 function MathTan(x) { 479 function MathTan(x) {
181 return MathSin(x) / MathCos(x); 480 x = x * 1; // Convert to number and deal with -0.
182 } 481 if (%_IsMinusZero(x)) return x;
183 482 if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) {
184 // Non-standard extension. 483 // |x| < pi/4, approximately. No reduction needed.
185 function MathImul(x, y) { 484 return KernelTan(x, 0, 1);
186 return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y)); 485 }
187 } 486 return MathTanSlow(x);
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 } 487 }
256 488
257 // ------------------------------------------------------------------- 489 // -------------------------------------------------------------------
258 490
259 function SetUpMath() { 491 function SetUpMath() {
260 %CheckIsBootstrapping(); 492 %CheckIsBootstrapping();
261 493
262 %SetPrototype($Math, $Object.prototype); 494 %SetPrototype($Math, $Object.prototype);
263 %SetProperty(global, "Math", $Math, DONT_ENUM); 495 %SetProperty(global, "Math", $Math, DONT_ENUM);
264 %FunctionSetInstanceClassName(MathConstructor, 'Math'); 496 %FunctionSetInstanceClassName(MathConstructor, 'Math');
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 "max", MathMax, 533 "max", MathMax,
302 "min", MathMin, 534 "min", MathMin,
303 "imul", MathImul 535 "imul", MathImul
304 )); 536 ));
305 537
306 %SetInlineBuiltinFlag(MathCeil); 538 %SetInlineBuiltinFlag(MathCeil);
307 %SetInlineBuiltinFlag(MathRandom); 539 %SetInlineBuiltinFlag(MathRandom);
308 %SetInlineBuiltinFlag(MathSin); 540 %SetInlineBuiltinFlag(MathSin);
309 %SetInlineBuiltinFlag(MathCos); 541 %SetInlineBuiltinFlag(MathCos);
310 %SetInlineBuiltinFlag(MathTan); 542 %SetInlineBuiltinFlag(MathTan);
311 %SetInlineBuiltinFlag(TrigonometricInterpolation); 543 %SetInlineBuiltinFlag(KernelSin);
544 %SetInlineBuiltinFlag(KernelCos);
545 %SetInlineBuiltinFlag(KernelTan);
312 } 546 }
313 547
314 SetUpMath(); 548 SetUpMath();
OLDNEW
« no previous file with comments | « src/bootstrapper.cc ('k') | src/rempio2.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698