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

Side by Side Diff: src/math.js

Issue 411263004: Implement trigonometric functions using a fdlibm port. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: addressed comment Created 6 years, 5 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
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
168 function 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 161 // ECMA 262 - 15.8.2.17
175 function MathSqrt(x) { 162 function MathSqrt(x) {
176 return %_MathSqrtRT(TO_NUMBER_INLINE(x)); 163 return %_MathSqrtRT(TO_NUMBER_INLINE(x));
177 } 164 }
178 165
179 // ECMA 262 - 15.8.2.18
180 function MathTan(x) {
181 return MathSin(x) / MathCos(x);
182 }
183
184 // Non-standard extension. 166 // Non-standard extension.
185 function MathImul(x, y) { 167 function MathImul(x, y) {
186 return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y)); 168 return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
187 } 169 }
188 170
189 171 // -------------------------------------------------------------------
190 var kInversePiHalf = 0.636619772367581343; // 2 / pi 172
191 var kInversePiHalfS26 = 9.48637384723993156e-9; // 2 / pi / (2^26) 173 // A straightforward translation of fdlibm routines for sin, cos, and
192 var kS26 = 1 << 26; 174 // tan, by Raymond Toy (rtoy@google.com).
193 var kTwoStepThreshold = 1 << 27; 175
194 // pi / 2 rounded up 176 // ====================================================
195 var kPiHalf = 1.570796326794896780; // 0x192d4454fb21f93f 177 // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
196 // We use two parts for pi/2 to emulate a higher precision. 178 //
197 // pi_half_1 only has 26 significant bits for mantissa. 179 // Developed at SunSoft, a Sun Microsystems, Inc. business.
198 // Note that pi_half > pi_half_1 + pi_half_2 180 // Permission to use, copy, modify, and distribute this
199 var kPiHalf1 = 1.570796325802803040; // 0x00000054fb21f93f 181 // software is freely granted, provided that this notice
200 var kPiHalf2 = 9.920935796805404252e-10; // 0x3326a611460b113e 182 // is preserved.
201 183 // ====================================================
202 var kSamples; // Initialized to a number during genesis. 184
203 var kIndexConvert; // Initialized to kSamples / (pi/2) during genesis. 185 // Initialized to a Float64Array during genesis and is not writable.
204 var kSinTable; // Initialized to a Float64Array during genesis. 186 var kTrig;
205 var kCosXIntervalTable; // Initialized to a Float64Array during genesis. 187
206 188 macro REMPIO2(X)
Raymond Toy 2014/07/24 18:08:44 Does this have to be a macro? Dropping this big ch
Yang 2014/07/28 11:28:59 Yes. Calling a function with double arguments in V
Raymond Toy 2014/07/30 19:55:04 Ok, that's not too surprising if your tests stress
207 // This implements sine using the following algorithm. 189 var n, y0, y1;
208 // 1) Multiplication takes care of to-number conversion. 190 var hx = %_DoubleHi(X);
209 // 2) Reduce x to the first quadrant [0, pi/2]. 191 var ix = hx & 0x7fffffff;
210 // Conveniently enough, in case of +/-Infinity, we get NaN. 192
211 // Note that we try to use only 26 instead of 52 significant bits for 193 if (ix < 0x4002d97c) {
212 // mantissa to avoid rounding errors when multiplying. For very large 194 // |X| ~< 3*pi/4, special case with n = +/- 1
213 // input we therefore have additional steps. 195 if (hx > 0) {
214 // 3) Replace x by (pi/2-x) if x was in the 2nd or 4th quadrant. 196 var z = X - kTrig[1];
Raymond Toy 2014/07/24 16:51:11 Readability would be improved if the kTrig values
Yang 2014/07/28 11:28:59 That is true. However, given that we currently don
Raymond Toy 2014/07/30 19:55:04 Could you give symbolic names for the indices? So
Yang 2014/08/01 07:29:55 Done with macros
215 // 4) Do a table lookup for the closest samples to the left and right of x. 197 if (ix != 0x3ff921fb) {
216 // 5) Find the derivatives at those sampling points by table lookup: 198 // 33+53 bit pi is good enough
217 // dsin(x)/dx = cos(x) = sin(pi/2-x) for x in [0, pi/2]. 199 y0 = z - kTrig[2];
218 // 6) Use cubic spline interpolation to approximate sin(x). 200 y1 = (z - y0) - kTrig[2];
219 // 7) Negate the result if x was in the 3rd or 4th quadrant. 201 } else {
220 // 8) Get rid of -0 by adding 0. 202 // near pi/2, use 33+33+53 bit pi
221 function TrigonometricInterpolation(x, phase) { 203 z -= kTrig[3];
222 if (x < 0 || x > kPiHalf) { 204 y0 = z - kTrig[4];
223 var multiple; 205 y1 = (z - y0) - kTrig[4];
224 while (x < -kTwoStepThreshold || x > kTwoStepThreshold) { 206 }
225 // Let's assume this loop does not terminate. 207 n = 1;
226 // All numbers x in each loop forms a set S. 208 } else {
227 // (1) abs(x) > 2^27 for all x in S. 209 // Negative X
228 // (2) abs(multiple) != 0 since (2^27 * inverse_pi_half_s26) > 1 210 var z = X + kTrig[1];
229 // (3) multiple is rounded down in 2^26 steps, so the rounding error is 211 if (ix != 0x3ff921fb) {
230 // at most max(ulp, 2^26). 212 // 33+53 bit pi is good enough
231 // (4) so for x > 2^27, we subtract at most (1+pi/4)x and at least 213 y0 = z + kTrig[2];
232 // (1-pi/4)x 214 y1 = (z - y0) + kTrig[2];
233 // (5) The subtraction results in x' so that abs(x') <= abs(x)*pi/4. 215 } else {
234 // Note that this difference cannot be simply rounded off. 216 // near pi/2, use 33+33+53 bit pi
235 // Set S cannot exist since (5) violates (1). Loop must terminate. 217 z += kTrig[3];
236 multiple = MathFloor(x * kInversePiHalfS26) * kS26; 218 y0 = z + kTrig[4];
237 x = x - multiple * kPiHalf1 - multiple * kPiHalf2; 219 y1 = (z - y0) + kTrig[4];
238 } 220 }
239 multiple = MathFloor(x * kInversePiHalf); 221 n = -1;
240 x = x - multiple * kPiHalf1 - multiple * kPiHalf2; 222 }
241 phase += multiple; 223 } else if (ix <= 0x413921fb) {
242 } 224 // |X| ~<= 2^19*(pi/2), medium size
243 var double_index = x * kIndexConvert; 225 var t = MathAbs(X);
244 if (phase & 1) double_index = kSamples - double_index; 226 n = (t * kTrig[0] + 0.5) | 0;
245 var index = double_index | 0; 227 var r = t - n * kTrig[1];
246 var t1 = double_index - index; 228 var w = n * kTrig[2];
247 var t2 = 1 - t1; 229 // First round good to 85 bit
248 var y1 = kSinTable[index]; 230 y0 = r - w;
249 var y2 = kSinTable[index + 1]; 231 if (ix - (%_DoubleHi(y0) & 0x7ff00000) > 0x1000000) {
Raymond Toy 2014/07/30 19:55:04 It would certainly be good to document what this i
Yang 2014/08/01 07:29:55 I don't think we should be spending time documenti
Raymond Toy 2014/08/01 14:06:00 I would agree if this were in fact the original co
250 var dy = y2 - y1; 232 // 2nd iteration needed, good to 118
251 return (t2 * y1 + t1 * y2 + 233 t = r;
252 t1 * t2 * ((kCosXIntervalTable[index] - dy) * t2 + 234 w = n * kTrig[3];
253 (dy - kCosXIntervalTable[index + 1]) * t1)) 235 r = t - w;
254 * (1 - (phase & 2)) + 0; 236 w = n * kTrig[4] - ((t - r) - w);
255 } 237 y0 = r - w;
256 238 if (ix - (%_DoubleHi(y0) & 0x7ff00000) > 0x3100000) {
Raymond Toy 2014/07/30 19:55:03 Same comment as for line 231.
Yang 2014/08/01 07:29:55 Acknowledged.
257 239 // 3rd iteration needed. 151 bits accuracy
240 t = r;
241 w = n * kTrig[5];
242 r = t - w;
243 w = n * kTrig[6] - ((t - r) - w);
244 y0 = r - w;
245 }
246 }
247 y1 = (r - y0) - w;
248 if (hx < 0) {
249 n = -n;
250 y0 = -y0;
251 y1 = -y1;
252 }
253 } else {
254 // Need to do full Payne-Hanek reduction here!
255 var r = %RemPiO2(X);
256 n = r[0];
257 y0 = r[1];
258 y1 = r[2];
259 }
260 endmacro
261
262 // Sine for [-pi/4, pi/4], pi/4 ~ 0.7854
Raymond Toy 2014/07/24 16:51:11 I think it would be beneficial to add the original
Yang 2014/07/28 11:28:59 Done.
263 macro RETURN_KERNELSIN(X0, X1, SIGN)
264 var z = X0 * X0;
265 var v = z * X0;
266 var r = kTrig[8] + z * (kTrig[9] + z * (kTrig[10] +
267 z * (kTrig[11] + z * kTrig[12])));
268 return (X0 - ((z * (0.5 * X1 - v * r) - X1) - v * kTrig[7])) SIGN;
Raymond Toy 2014/07/24 16:51:11 The original kernel_sin function had a slight opti
Yang 2014/07/28 11:28:59 I think I checked this. And I rechecked. In neithe
Raymond Toy 2014/07/30 19:55:04 Fair enough.
269 endmacro
Raymond Toy 2014/07/24 16:51:12 I think readability would be enhanced if you had a
Yang 2014/07/28 11:28:59 Acknowledged.
270
271 // Cosine for [-pi/4, pi/4], pi/4 ~ 0.7854
272 macro RETURN_KERNELCOS(X0, X1, SIGN)
273 var ix = %_DoubleHi(X0) & 0x7fffffff;
274 var z = X0 * X0;
275 var r = z * (kTrig[13] + z * (kTrig[14] + z * (kTrig[15] +
Raymond Toy 2014/07/24 16:51:11 Can we use a separate array for kTrig values here
Yang 2014/07/28 11:28:59 There is not much readability to be gained here im
Raymond Toy 2014/07/30 19:55:04 I have no problems with the various random constan
Yang 2014/08/01 07:29:55 Done with macros.
276 z * (kTrig[16] + z * (kTrig[17] + z * kTrig[18])))));
277 if (ix < 0x3fd33333) {
Raymond Toy 2014/07/30 19:55:03 Add comment that ix < 0x3fd33333 is testing for |x
Yang 2014/08/01 07:29:55 Done.
278 return (1 - (0.5 * z - (z * r - X0 * X1))) SIGN;
279 } else {
280 var qx;
281 if (ix > 0x3fe90000) {
Raymond Toy 2014/07/24 16:51:12 I think it's really useful to include the fdlibm c
Yang 2014/07/28 11:28:59 imo the comment in your port doesn't explain much
Raymond Toy 2014/07/30 19:55:03 No, but at least it makes it clear that ix > 0x3fe
Yang 2014/08/01 07:29:55 Done.
282 qx = 0.28125;
283 } else {
284 qx = %_ConstructDouble(%_DoubleHi(0.25 * X0), 0);
285 }
286 var hz = 0.5 * z - qx;
287 return (1 - qx - (hz - (z * r - X0 * X1))) SIGN;
288 }
289 endmacro
290
291 // Tangent for [-pi/4, pi/4], pi/4 ~ 0.7854
Raymond Toy 2014/07/24 16:51:12 Include original fdlibm comment. Without that it's
Yang 2014/07/28 11:28:59 Done.
292 function KernelTan(x, y, returnTan) {
293 var z;
294 var w;
295 var hx = %_DoubleHi(x);
296 var ix = hx & 0x7fffffff;
297
298 if (ix < 0x3e300000) {
299 // x < 2^-28
300 if (((ix | %_DoubleLo(x)) | (returnTan + 1)) == 0) {
Raymond Toy 2014/07/30 19:55:04 This might be more obvious if we said abs(x) == 0
Yang 2014/08/01 07:29:55 I don't think I changed anything here from your po
301 return 1 / MathAbs(x);
302 } else {
303 if (returnTan == 1) {
304 return x;
305 } else {
306 // Compute -1/(x + y) carefully
307 var w = x + y;
308 var z = %_ConstructDouble(%_DoubleHi(w), 0);
309 var v = y - (z - x);
310 var a = -1 / w;
311 var t = %_ConstructDouble(%_DoubleHi(a), 0);
312 var s = 1 + t * z;
313 return t + a * (s + t * v);
314 }
315 }
316 }
317 if (ix >= 0x3fe59429) {
318 // |x| > .6744
319 if (x < 0) {
320 x = -x;
321 y = -y;
322 }
323 z = kTrig[32] - x;
324 w = kTrig[33] - y;
325 x = z + w;
326 y = 0;
327 }
328 z = x * x;
329 w = z * z;
330
331 var r = kTrig[20] + w * (kTrig[22] + w * (kTrig[24] +
Raymond Toy 2014/07/24 16:51:11 Can we use a separate array for the coefficients i
Yang 2014/07/28 11:28:59 Acknowledged.
Raymond Toy 2014/07/30 19:55:04 Add back the comment. This would be unexpected fro
332 w * (kTrig[26] + w * (kTrig[28] + w * kTrig[30]))));
333 var v = z * (kTrig[21] + w * (kTrig[23] + w * (kTrig[25] +
334 w * (kTrig[27] + w * (kTrig[29] + w * kTrig[31])))));
335 var s = z * x;
336 r = y + z * (s * (r + v) + y);
337 r = r + kTrig[19] * s;
338 w = x + r;
339 if (ix >= 0x3fe59428) {
Raymond Toy 2014/07/30 19:55:04 Document what 0x3fe59428 is.
340 return (1 - ((hx >> 30) & 2)) *
Raymond Toy 2014/07/30 19:55:04 Describe what ((hx >> 30) & 2) is doing.
341 (returnTan - 2.0 * (x - (w * w / (w + returnTan) - r)));
342 }
343 if (returnTan == 1) {
344 return w;
345 } else {
346 z = %_ConstructDouble(%_DoubleHi(w), 0);
347 v = r - (z - x);
348 var a = -1 / w;
349 var t = %_ConstructDouble(%_DoubleHi(a), 0);
350 s = 1 + t * z;
351 return t + a * (s + t * v);
352 }
353 }
354
355 function MathSinSlow(x) {
Raymond Toy 2014/07/24 16:51:12 I think MathSinAccurate is a better name. Or maybe
Yang 2014/07/28 11:28:59 This function is not more accurate. The difference
Raymond Toy 2014/07/30 19:55:03 Sure, that makes sense.
356 REMPIO2(x);
357 var sign = 1 - (n & 2);
358 if (n & 1) {
359 RETURN_KERNELCOS(y0, y1, * sign);
360 } else {
361 RETURN_KERNELSIN(y0, y1, * sign);
362 }
363 }
364
365 function MathCosSlow(x) {
Raymond Toy 2014/07/24 16:51:11 Similar comment as MathSinSlow.
Yang 2014/07/28 11:28:59 Acknowledged.
366 REMPIO2(x);
367 if (n & 1) {
368 var sign = (n & 2) - 1;
369 RETURN_KERNELSIN(y0, y1, * sign);
370 } else {
371 var sign = 1 - (n & 2);
372 RETURN_KERNELCOS(y0, y1, * sign);
373 }
374 }
375
376 // ECMA 262 - 15.8.2.16
377 function MathSin(x) {
378 x = x * 1; // Convert to number.
379 if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) {
380 // |x| < pi/4, approximately. No reduction needed.
381 if (%_IsMinusZero(x)) return x;
Raymond Toy 2014/07/24 18:08:44 Is this test for -0 necessary? KERNELSIN should be
Yang 2014/07/28 11:28:59 You are right. Removed.
382 RETURN_KERNELSIN(x, 0, /* empty */);
383 }
384 return MathSinSlow(x);
385 }
386
387 // ECMA 262 - 15.8.2.7
388 function MathCos(x) {
389 x = x * 1; // Convert to number.
390 if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) {
391 // |x| < pi/4, approximately. No reduction needed.
392 RETURN_KERNELCOS(x, 0, /* empty */);
393 }
394 return MathCosSlow(x);
395 }
396
397 // ECMA 262 - 15.8.2.18
398 function MathTan(x) {
399 x = x * 1; // Convert to number.
400 if ((%_DoubleHi(x) & 0x7fffffff) <= 0x3fe921fb) {
401 // |x| < pi/4, approximately. No reduction needed.
402 if (%_IsMinusZero(x)) return x;
Raymond Toy 2014/07/24 18:08:43 Is this test for -0 really necessary? If KernelTan
Yang 2014/07/28 11:28:59 Done.
403 return KernelTan(x, 0, 1);
404 }
405 REMPIO2(x);
406 return KernelTan(y0, y1, (n & 1) ? -1 : 1);
407 }
408
409
258 // ES6 draft 09-27-13, section 20.2.2.28. 410 // ES6 draft 09-27-13, section 20.2.2.28.
259 function MathSign(x) { 411 function MathSign(x) {
260 x = TO_NUMBER_INLINE(x); 412 x = TO_NUMBER_INLINE(x);
261 if (x > 0) return 1; 413 if (x > 0) return 1;
262 if (x < 0) return -1; 414 if (x < 0) return -1;
263 if (x === 0) return x; 415 if (x === 0) return x;
264 return NAN; 416 return NAN;
265 } 417 }
266 418
267 419
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after
530 "clz32", MathClz32, 682 "clz32", MathClz32,
531 "cbrt", MathCbrt, 683 "cbrt", MathCbrt,
532 "log1p", MathLog1p, 684 "log1p", MathLog1p,
533 "expm1", MathExpm1 685 "expm1", MathExpm1
534 )); 686 ));
535 687
536 %SetInlineBuiltinFlag(MathCeil); 688 %SetInlineBuiltinFlag(MathCeil);
537 %SetInlineBuiltinFlag(MathRandom); 689 %SetInlineBuiltinFlag(MathRandom);
538 %SetInlineBuiltinFlag(MathSin); 690 %SetInlineBuiltinFlag(MathSin);
539 %SetInlineBuiltinFlag(MathCos); 691 %SetInlineBuiltinFlag(MathCos);
540 %SetInlineBuiltinFlag(MathTan);
541 %SetInlineBuiltinFlag(TrigonometricInterpolation);
542 } 692 }
543 693
544 SetUpMath(); 694 SetUpMath();
OLDNEW
« no previous file with comments | « src/bootstrapper.cc ('k') | src/rempio2.h » ('j') | src/rempio2.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698