|
1 |
| -// Note: Instead of "double frexp(double arg, int* exp)" this is built as "[double, int] frexp(double arg)" due to ECMAScripts's lack of pointers |
2 |
| -// A hypothetical issue with this implementation is that the precision the ** operator is not defined in the ECMAScript standard, |
3 |
| -// however, sane ECMAScript implementations should give precise results for 2**<integer> expressions |
4 |
| -// Cppreference: https://en.cppreference.com/w/c/numeric/math/frexp for a more detailed description |
| 1 | +export interface frexp_result { |
| 2 | + exponent: number; |
| 3 | + fraction: number; |
| 4 | +} |
| 5 | + |
| 6 | +// Note: Instead of "double frexp(double arg, int* exp)" this is built as "{ exponent, fraction } frexp(double arg)" due to ECMAScripts's lack of pointers |
| 7 | +// A hypothetical issue with this implementation is that the precision of the ** operator is not defined in the ECMAScript standard, |
| 8 | +// however, it is hard to imagine a sane ECMAScript implementation would give imprecise results for 2**<integer> expressions. |
| 9 | +// Cppreference: https://en.cppreference.com/w/cpp/numeric/math/frexp |
5 | 10 | // Object.is(n, frexp(n)[0] * 2 ** frexp(n)[1]) for all number values of n except when Math.isFinite(n) && Math.abs(n) > 2**1023
|
6 | 11 | // Object.is(n, (2 * frexp(n)[0]) * 2 ** (frexp(n)[1] - 1)) for all number values of n
|
7 | 12 | // Object.is(n, frexp(n)[0]) for these values of n: 0, -0, NaN, Infinity, -Infinity
|
8 | 13 | // Math.abs(frexp(n)[0]) is >= 0.5 and < 1.0 for any other number-type value of n
|
9 |
| -export function frexp( |
10 |
| - /*double*/ num: number, |
11 |
| -): [/*double*/ fraction: number, /*int*/ exponent: number] { |
| 14 | +export function frexp(num: number): frexp_result { |
12 | 15 | if (num === 0 || !Number.isFinite(num)) {
|
13 |
| - return [num, 0]; |
| 16 | + return { exponent: 0, fraction: num }; |
14 | 17 | }
|
15 | 18 |
|
16 | 19 | const absNum: number = Math.abs(num);
|
17 | 20 |
|
18 |
| - let exp: number = Math.max(-1023, Math.floor(Math.log2(absNum)) + 1); |
19 |
| - let x: number = absNum * 2 ** -exp; |
| 21 | + let exponent: number = Math.max(-1023, Math.floor(Math.log2(absNum)) + 1); |
| 22 | + let fraction: number = absNum * 2 ** -exponent; |
20 | 23 |
|
21 | 24 | // These while loops compensate for rounding errors that may occur because of ECMAScript's Math.log2's undefined precision
|
22 | 25 | // and the first one also helps work around the issue of 2 ** -exp === Infinity when exp <= -1024
|
23 |
| - while (x < 0.5) { |
24 |
| - x *= 2; |
25 |
| - --exp; |
| 26 | + while (fraction < 0.5) { |
| 27 | + fraction *= 2; |
| 28 | + --exponent; |
26 | 29 | }
|
27 | 30 |
|
28 | 31 | // istanbul ignore next - This might never run and that's okay. See the above comment
|
29 |
| - while (x >= 1) { |
30 |
| - x *= 0.5; |
31 |
| - ++exp; |
| 32 | + while (fraction >= 1) { |
| 33 | + fraction *= 0.5; |
| 34 | + ++exponent; |
32 | 35 | }
|
33 | 36 |
|
34 | 37 | if (num < 0) {
|
35 |
| - x = -x; |
| 38 | + fraction = -fraction; |
36 | 39 | }
|
37 | 40 |
|
38 |
| - return [x, exp]; |
| 41 | + return { |
| 42 | + exponent, |
| 43 | + fraction, |
| 44 | + }; |
39 | 45 | }
|
0 commit comments