OLD | NEW |
---|---|
1 // The following is adapted from fdlibm (http://www.netlib.org/fdlibm), | 1 // The following is adapted from fdlibm (http://www.netlib.org/fdlibm), |
2 // | 2 // |
3 // ==================================================== | 3 // ==================================================== |
4 // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. | 4 // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. |
5 // | 5 // |
6 // Developed at SunSoft, a Sun Microsystems, Inc. business. | 6 // Developed at SunSoft, a Sun Microsystems, Inc. business. |
7 // Permission to use, copy, modify, and distribute this | 7 // Permission to use, copy, modify, and distribute this |
8 // software is freely granted, provided that this notice | 8 // software is freely granted, provided that this notice |
9 // is preserved. | 9 // is preserved. |
10 // ==================================================== | 10 // ==================================================== |
11 // | 11 // |
12 // The original source code covered by the above license above has been | 12 // The original source code covered by the above license above has been |
13 // modified significantly by Google Inc. | 13 // modified significantly by Google Inc. |
14 // Copyright 2014 the V8 project authors. All rights reserved. | 14 // Copyright 2014 the V8 project authors. All rights reserved. |
15 // | 15 // |
16 // The following is a straightforward translation of fdlibm routines for | 16 // The following is a straightforward translation of fdlibm routines for |
17 // sin, cos, and tan, by Raymond Toy (rtoy@google.com). | 17 // sin, cos, and tan, by Raymond Toy (rtoy@google.com). |
Raymond Toy
2014/08/08 17:32:17
Update to say log1p is also done? Or maybe just re
Yang
2014/08/11 07:51:06
Done.
| |
18 | 18 |
19 | 19 |
20 var kTrig; // Initialized to a Float64Array during genesis and is not writable. | 20 var kMath; // Initialized to a Float64Array during genesis and is not writable. |
21 | 21 |
22 const INVPIO2 = kTrig[0]; | 22 const INVPIO2 = kMath[0]; |
23 const PIO2_1 = kTrig[1]; | 23 const PIO2_1 = kMath[1]; |
24 const PIO2_1T = kTrig[2]; | 24 const PIO2_1T = kMath[2]; |
25 const PIO2_2 = kTrig[3]; | 25 const PIO2_2 = kMath[3]; |
26 const PIO2_2T = kTrig[4]; | 26 const PIO2_2T = kMath[4]; |
27 const PIO2_3 = kTrig[5]; | 27 const PIO2_3 = kMath[5]; |
28 const PIO2_3T = kTrig[6]; | 28 const PIO2_3T = kMath[6]; |
29 const PIO4 = kTrig[32]; | 29 const PIO4 = kMath[32]; |
30 const PIO4LO = kTrig[33]; | 30 const PIO4LO = kMath[33]; |
31 | 31 |
32 // Compute k and r such that x - k*pi/2 = r where |r| < pi/4. For | 32 // Compute k and r such that x - k*pi/2 = r where |r| < pi/4. For |
33 // precision, r is returned as two values y0 and y1 such that r = y0 + y1 | 33 // precision, r is returned as two values y0 and y1 such that r = y0 + y1 |
34 // to more than double precision. | 34 // to more than double precision. |
35 macro REMPIO2(X) | 35 macro REMPIO2(X) |
36 var n, y0, y1; | 36 var n, y0, y1; |
37 var hx = %_DoubleHi(X); | 37 var hx = %_DoubleHi(X); |
38 var ix = hx & 0x7fffffff; | 38 var ix = hx & 0x7fffffff; |
39 | 39 |
40 if (ix < 0x4002d97c) { | 40 if (ix < 0x4002d97c) { |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
126 // | 126 // |
127 // 3. ieee_sin(X+Y) = ieee_sin(X) + sin'(X')*Y | 127 // 3. ieee_sin(X+Y) = ieee_sin(X) + sin'(X')*Y |
128 // ~ ieee_sin(X) + (1-X*X/2)*Y | 128 // ~ ieee_sin(X) + (1-X*X/2)*Y |
129 // For better accuracy, let | 129 // For better accuracy, let |
130 // 3 2 2 2 2 | 130 // 3 2 2 2 2 |
131 // r = X *(S2+X *(S3+X *(S4+X *(S5+X *S6)))) | 131 // r = X *(S2+X *(S3+X *(S4+X *(S5+X *S6)))) |
132 // then 3 2 | 132 // then 3 2 |
133 // sin(x) = X + (S1*X + (X *(r-Y/2)+Y)) | 133 // sin(x) = X + (S1*X + (X *(r-Y/2)+Y)) |
134 // | 134 // |
135 macro KSIN(x) | 135 macro KSIN(x) |
136 kTrig[7+x] | 136 kMath[7+x] |
137 endmacro | 137 endmacro |
138 | 138 |
139 macro RETURN_KERNELSIN(X, Y, SIGN) | 139 macro RETURN_KERNELSIN(X, Y, SIGN) |
140 var z = X * X; | 140 var z = X * X; |
141 var v = z * X; | 141 var v = z * X; |
142 var r = KSIN(1) + z * (KSIN(2) + z * (KSIN(3) + | 142 var r = KSIN(1) + z * (KSIN(2) + z * (KSIN(3) + |
143 z * (KSIN(4) + z * KSIN(5)))); | 143 z * (KSIN(4) + z * KSIN(5)))); |
144 return (X - ((z * (0.5 * Y - v * r) - Y) - v * KSIN(0))) SIGN; | 144 return (X - ((z * (0.5 * Y - v * r) - Y) - v * KSIN(0))) SIGN; |
145 endmacro | 145 endmacro |
146 | 146 |
(...skipping 23 matching lines...) Expand all Loading... | |
170 // cos(X+Y) = 1 - (X*X/2 - (r - X*Y)) | 170 // cos(X+Y) = 1 - (X*X/2 - (r - X*Y)) |
171 // For better accuracy when x > 0.3, let qx = |x|/4 with | 171 // For better accuracy when x > 0.3, let qx = |x|/4 with |
172 // the last 32 bits mask off, and if x > 0.78125, let qx = 0.28125. | 172 // the last 32 bits mask off, and if x > 0.78125, let qx = 0.28125. |
173 // Then | 173 // Then |
174 // cos(X+Y) = (1-qx) - ((X*X/2-qx) - (r-X*Y)). | 174 // cos(X+Y) = (1-qx) - ((X*X/2-qx) - (r-X*Y)). |
175 // Note that 1-qx and (X*X/2-qx) is EXACT here, and the | 175 // Note that 1-qx and (X*X/2-qx) is EXACT here, and the |
176 // magnitude of the latter is at least a quarter of X*X/2, | 176 // magnitude of the latter is at least a quarter of X*X/2, |
177 // thus, reducing the rounding error in the subtraction. | 177 // thus, reducing the rounding error in the subtraction. |
178 // | 178 // |
179 macro KCOS(x) | 179 macro KCOS(x) |
180 kTrig[13+x] | 180 kMath[13+x] |
181 endmacro | 181 endmacro |
182 | 182 |
183 macro RETURN_KERNELCOS(X, Y, SIGN) | 183 macro RETURN_KERNELCOS(X, Y, SIGN) |
184 var ix = %_DoubleHi(X) & 0x7fffffff; | 184 var ix = %_DoubleHi(X) & 0x7fffffff; |
185 var z = X * X; | 185 var z = X * X; |
186 var r = z * (KCOS(0) + z * (KCOS(1) + z * (KCOS(2)+ | 186 var r = z * (KCOS(0) + z * (KCOS(1) + z * (KCOS(2)+ |
187 z * (KCOS(3) + z * (KCOS(4) + z * KCOS(5)))))); | 187 z * (KCOS(3) + z * (KCOS(4) + z * KCOS(5)))))); |
188 if (ix < 0x3fd33333) { // |x| ~< 0.3 | 188 if (ix < 0x3fd33333) { // |x| ~< 0.3 |
189 return (1 - (0.5 * z - (z * r - X * Y))) SIGN; | 189 return (1 - (0.5 * z - (z * r - X * Y))) SIGN; |
190 } else { | 190 } else { |
191 var qx; | 191 var qx; |
192 if (ix > 0x3fe90000) { // |x| > 0.78125 | 192 if (ix > 0x3fe90000) { // |x| > 0.78125 |
193 qx = 0.28125; | 193 qx = 0.28125; |
194 } else { | 194 } else { |
195 qx = %_ConstructDouble(%_DoubleHi(0.25 * X), 0); | 195 qx = %_ConstructDouble(%_DoubleHi(0.25 * X), 0); |
196 } | 196 } |
197 var hz = 0.5 * z - qx; | 197 var hz = 0.5 * z - qx; |
198 return (1 - qx - (hz - (z * r - X * Y))) SIGN; | 198 return (1 - qx - (hz - (z * r - X * Y))) SIGN; |
199 } | 199 } |
200 endmacro | 200 endmacro |
201 | 201 |
202 | |
202 // kernel tan function on [-pi/4, pi/4], pi/4 ~ 0.7854 | 203 // kernel tan function on [-pi/4, pi/4], pi/4 ~ 0.7854 |
203 // Input x is assumed to be bounded by ~pi/4 in magnitude. | 204 // Input x is assumed to be bounded by ~pi/4 in magnitude. |
204 // Input y is the tail of x. | 205 // Input y is the tail of x. |
205 // Input k indicates whether ieee_tan (if k = 1) or -1/tan (if k = -1) | 206 // Input k indicates whether ieee_tan (if k = 1) or -1/tan (if k = -1) |
206 // is returned. | 207 // is returned. |
207 // | 208 // |
208 // Algorithm | 209 // Algorithm |
209 // 1. Since ieee_tan(-x) = -ieee_tan(x), we need only to consider positive x. | 210 // 1. Since ieee_tan(-x) = -ieee_tan(x), we need only to consider positive x. |
210 // 2. if x < 2^-28 (hx<0x3e300000 0), return x with inexact if x!=0. | 211 // 2. if x < 2^-28 (hx<0x3e300000 0), return x with inexact if x!=0. |
211 // 3. ieee_tan(x) is approximated by a odd polynomial of degree 27 on | 212 // 3. ieee_tan(x) is approximated by a odd polynomial of degree 27 on |
(...skipping 16 matching lines...) Expand all Loading... | |
228 // tan(x+y) = x + (T1*x + (x *(r+y)+y)) | 229 // tan(x+y) = x + (T1*x + (x *(r+y)+y)) |
229 // | 230 // |
230 // 4. For x in [0.67434,pi/4], let y = pi/4 - x, then | 231 // 4. For x in [0.67434,pi/4], let y = pi/4 - x, then |
231 // tan(x) = ieee_tan(pi/4-y) = (1-ieee_tan(y))/(1+ieee_tan(y)) | 232 // tan(x) = ieee_tan(pi/4-y) = (1-ieee_tan(y))/(1+ieee_tan(y)) |
232 // = 1 - 2*(ieee_tan(y) - (ieee_tan(y)^2)/(1+ieee_tan(y))) | 233 // = 1 - 2*(ieee_tan(y) - (ieee_tan(y)^2)/(1+ieee_tan(y))) |
233 // | 234 // |
234 // Set returnTan to 1 for tan; -1 for cot. Anything else is illegal | 235 // Set returnTan to 1 for tan; -1 for cot. Anything else is illegal |
235 // and will cause incorrect results. | 236 // and will cause incorrect results. |
236 // | 237 // |
237 macro KTAN(x) | 238 macro KTAN(x) |
238 kTrig[19+x] | 239 kMath[19+x] |
239 endmacro | 240 endmacro |
240 | 241 |
241 function KernelTan(x, y, returnTan) { | 242 function KernelTan(x, y, returnTan) { |
242 var z; | 243 var z; |
243 var w; | 244 var w; |
244 var hx = %_DoubleHi(x); | 245 var hx = %_DoubleHi(x); |
245 var ix = hx & 0x7fffffff; | 246 var ix = hx & 0x7fffffff; |
246 | 247 |
247 if (ix < 0x3e300000) { // |x| < 2^-28 | 248 if (ix < 0x3e300000) { // |x| < 2^-28 |
248 if (((ix | %_DoubleLo(x)) | (returnTan + 1)) == 0) { | 249 if (((ix | %_DoubleLo(x)) | (returnTan + 1)) == 0) { |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
347 // ECMA 262 - 15.8.2.18 | 348 // ECMA 262 - 15.8.2.18 |
348 function MathTan(x) { | 349 function MathTan(x) { |
349 x = x * 1; // Convert to number. | 350 x = x * 1; // Convert to number. |
350 if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) { | 351 if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) { |
351 // |x| < pi/4, approximately. No reduction needed. | 352 // |x| < pi/4, approximately. No reduction needed. |
352 return KernelTan(x, 0, 1); | 353 return KernelTan(x, 0, 1); |
353 } | 354 } |
354 REMPIO2(x); | 355 REMPIO2(x); |
355 return KernelTan(y0, y1, (n & 1) ? -1 : 1); | 356 return KernelTan(y0, y1, (n & 1) ? -1 : 1); |
356 } | 357 } |
358 | |
359 // ES6 draft 09-27-13, section 20.2.2.20. | |
360 // Math.log1p | |
361 // | |
362 // Method : | |
363 // 1. Argument Reduction: find k and f such that | |
364 // 1+x = 2^k * (1+f), | |
365 // where sqrt(2)/2 < 1+f < sqrt(2) . | |
366 // | |
367 // Note. If k=0, then f=x is exact. However, if k!=0, then f | |
368 // may not be representable exactly. In that case, a correction | |
369 // term is need. Let u=1+x rounded. Let c = (1+x)-u, then | |
370 // log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u), | |
371 // and add back the correction term c/u. | |
372 // (Note: when x > 2**53, one can simply return log(x)) | |
373 // | |
374 // 2. Approximation of log1p(f). | |
375 // Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) | |
376 // = 2s + 2/3 s**3 + 2/5 s**5 + ....., | |
377 // = 2s + s*R | |
378 // We use a special Reme algorithm on [0,0.1716] to generate | |
379 // a polynomial of degree 14 to approximate R The maximum error | |
380 // of this polynomial approximation is bounded by 2**-58.45. In | |
381 // other words, | |
382 // 2 4 6 8 10 12 14 | |
383 // R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s +Lp6*s +Lp7*s | |
384 // (the values of Lp1 to Lp7 are listed in the program) | |
385 // and | |
386 // | 2 14 | -58.45 | |
387 // | Lp1*s +...+Lp7*s - R(z) | <= 2 | |
388 // | | | |
389 // Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. | |
390 // In order to guarantee error in log below 1ulp, we compute log | |
391 // by | |
392 // log1p(f) = f - (hfsq - s*(hfsq+R)). | |
393 // | |
394 // 3. Finally, log1p(x) = k*ln2 + log1p(f). | |
395 // = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) | |
396 // Here ln2 is split into two floating point number: | |
397 // ln2_hi + ln2_lo, | |
398 // where n*ln2_hi is always exact for |n| < 2000. | |
399 // | |
400 // Special cases: | |
401 // log1p(x) is NaN with signal if x < -1 (including -INF) ; | |
402 // log1p(+INF) is +INF; log1p(-1) is -INF with signal; | |
403 // log1p(NaN) is that NaN with no signal. | |
404 // | |
405 // Accuracy: | |
406 // according to an error analysis, the error is always less than | |
407 // 1 ulp (unit in the last place). | |
408 // | |
409 // Constants: | |
410 // The hexadecimal values are the intended ones for the following | |
411 // constants. The decimal values may be used, provided that the | |
412 // compiler will convert from decimal to binary accurately enough | |
413 // to produce the hexadecimal values shown. | |
414 // | |
415 // Note: Assuming log() return accurate answer, the following | |
416 // algorithm can be used to compute log1p(x) to within a few ULP: | |
417 // | |
418 // u = 1+x; | |
419 // if (u==1.0) return x ; else | |
420 // return log(u)*(x/(u-1.0)); | |
421 // | |
422 // See HP-15C Advanced Functions Handbook, p.193. | |
423 // | |
424 const LN2_HI = kMath[34]; | |
425 const LN2_LO = kMath[35]; | |
426 const TWO54 = kMath[36]; | |
427 const TWO_THIRD = kMath[37]; | |
Raymond Toy
2014/08/08 17:32:17
I'm curious on why you added these constants here
Raymond Toy
2014/08/08 17:40:30
Never mind. You actually did do this for the trig
| |
428 macro KLOGP1(x) | |
429 (kMath[38+x]) | |
430 endmacro | |
431 | |
432 function MathLog1p(x) { | |
433 x = x * 1; // Convert to number. | |
434 var hx = %_DoubleHi(x); | |
435 var ax = hx & 0x7fffffff; | |
436 var k = 1; | |
437 var f = 0; | |
Raymond Toy
2014/08/08 17:32:16
Perhaps initialize f to x here and remove the assi
Yang
2014/08/11 07:51:06
Done.
| |
438 var hu = 1; | |
439 var c = 0; | |
440 var u = 0; | |
Raymond Toy
2014/08/08 17:32:17
Maybe intialize u to x here and remove the assignm
Yang
2014/08/11 07:51:06
Done.
| |
441 | |
442 if (hx < 0x3fda827a) { | |
443 // x < 0.41422 | |
444 if (ax >= 0x3ff00000) { | |
Raymond Toy
2014/08/08 17:32:17
I would add a comment that |x| >= 1. (Really hard
Yang
2014/08/11 07:51:06
Done.
| |
445 if (x === -1) { | |
446 return -INFINITY; // log1p(-1) = -inf | |
447 } else { | |
448 return NAN; // log1p(x<-1) = NaN | |
449 } | |
450 } | |
451 if (ax < 0x3e200000) { | |
452 // |x| < 2^-29 | |
453 if ((TWO54 + x > 0) && (ax < 0x3c900000)) { | |
Raymond Toy
2014/08/08 17:32:17
The original fdlibm code said two54+x > 0 was mean
Yang
2014/08/11 07:51:07
Done.
| |
454 // |x| < 2^-54, so just return x | |
Raymond Toy
2014/08/08 17:32:17
Probably should update the comment to say
// |x|
Yang
2014/08/11 07:51:06
Done.
| |
455 return x; | |
456 } else { | |
457 return x - x * x * 0.5; | |
458 } | |
459 } | |
460 // (int) 0xbfd2bec3 = -0x402d413d | |
461 if ((hx > 0) || (hx <= -0x402D413D)) { | |
462 // -.2929 < x < 0.41422 | |
463 k = 0; | |
464 f = x; | |
465 hu = 1; | |
Raymond Toy
2014/08/08 17:32:17
Line 438 initializes hu to 1, so this can probably
Yang
2014/08/11 07:51:06
Done.
| |
466 } | |
467 } | |
468 | |
469 if (hx >= 0x7ff00000) return x + x; | |
Raymond Toy
2014/08/08 17:32:17
Add comment to say this handles infinities and NaN
Yang
2014/08/11 07:51:06
-Infinity is actually caught further up. This only
| |
470 | |
471 if (k !== 0) { | |
472 if (hx < 0x43400000) { | |
473 // x < 9.007199254740992e15 | |
Raymond Toy
2014/08/08 17:32:18
Maybe note that 9.007...e15 is actually exactly 2^
Yang
2014/08/11 07:51:06
Done.
| |
474 u = 1 + x; | |
475 hu = %_DoubleHi(u); | |
476 k = (hu >> 20) - 1023; | |
477 c = (k > 0) ? 1 - (u - x) : x - (u - 1); | |
478 c = c / u; | |
479 } else { | |
480 u = x; | |
481 hu = %_DoubleHi(u); | |
482 k = (hu >> 20) - 1023; | |
483 c = 0; | |
Raymond Toy
2014/08/08 17:32:17
Since c is initialized to 0 in line 439, perhaps w
Yang
2014/08/11 07:51:06
Done.
| |
484 } | |
485 hu = hu & 0xfffff; | |
486 if (hu < 0x6a09e) { | |
487 // Normalize u | |
488 u = %_ConstructDouble(hu | 0x3ff00000, %_DoubleLo(u)); | |
489 } else { | |
490 ++k; | |
491 u = %_ConstructDouble(hu | 0x3fe00000, %_DoubleLo(u)); | |
Raymond Toy
2014/08/08 17:32:17
fdlibm said this is normalizing u/2, so probably s
Yang
2014/08/11 07:51:06
Done.
| |
492 hu = (0x00100000 - hu) >> 2; | |
493 } | |
494 f = u - 1; | |
495 } | |
496 | |
497 var hfsq = 0.5 * f * f; | |
498 if (hu === 0) { | |
499 // |f| < 2^-20; | |
500 if (f === 0) { | |
501 if (k === 0) { | |
502 return 0.0; | |
503 } else { | |
504 return k * LN2_HI + (c + k * LN2_LO); | |
505 } | |
506 } | |
507 var R = hfsq * (1 - TWO_THIRD * f); | |
508 if (k === 0) { | |
509 return f - R; | |
510 } else { | |
511 return k * LN2_HI - ((R - (k * LN2_LO + c)) - f); | |
512 } | |
513 } | |
514 | |
515 var s = f / (2 + f); | |
516 var z = s * s; | |
517 var R = z * (KLOGP1(0) + z * (KLOGP1(1) + z * | |
518 (KLOGP1(2) + z * (KLOGP1(3) + z * | |
519 (KLOGP1(4) + z * (KLOGP1(5) + z * KLOGP1(6))))))); | |
520 if (k === 0) { | |
521 return f - (hfsq - s * (hfsq + R)); | |
522 } else { | |
523 return k * LN2_HI - ((hfsq - (s * (hfsq + R) + (k * LN2_LO + c))) - f); | |
524 } | |
525 } | |
OLD | NEW |