|
1 // The nearest representable values to +1.0. |
|
2 const ONE_PLUS_EPSILON = 1 + Math.pow(2, -52); // 0.9999999999999999 |
|
3 const ONE_MINUS_EPSILON = 1 - Math.pow(2, -53); // 1.0000000000000002 |
|
4 |
|
5 { |
|
6 var fail = function (msg) { |
|
7 var exc = new Error(msg); |
|
8 try { |
|
9 // Try to improve on exc.fileName and .lineNumber; leave exc.stack |
|
10 // alone. We skip two frames: fail() and its caller, an assertX() |
|
11 // function. |
|
12 var frames = exc.stack.trim().split("\n"); |
|
13 if (frames.length > 2) { |
|
14 var m = /@([^@:]*):([0-9]+)$/.exec(frames[2]); |
|
15 if (m) { |
|
16 exc.fileName = m[1]; |
|
17 exc.lineNumber = +m[2]; |
|
18 } |
|
19 } |
|
20 } catch (ignore) { throw ignore;} |
|
21 throw exc; |
|
22 }; |
|
23 |
|
24 var ENDIAN; // 0 for little-endian, 1 for big-endian. |
|
25 |
|
26 // Return the difference between the IEEE 754 bit-patterns for a and b. |
|
27 // |
|
28 // This is meaningful when a and b are both finite and have the same |
|
29 // sign. Then the following hold: |
|
30 // |
|
31 // * If a === b, then diff(a, b) === 0. |
|
32 // |
|
33 // * If a !== b, then diff(a, b) === 1 + the number of representable values |
|
34 // between a and b. |
|
35 // |
|
36 var f = new Float64Array([0, 0]); |
|
37 var u = new Uint32Array(f.buffer); |
|
38 var diff = function (a, b) { |
|
39 f[0] = a; |
|
40 f[1] = b; |
|
41 //print(u[1].toString(16) + u[0].toString(16) + " " + u[3].toString(16) + u[2].toString(16)); |
|
42 return Math.abs((u[3-ENDIAN] - u[1-ENDIAN]) * 0x100000000 + u[2+ENDIAN] - u[0+ENDIAN]); |
|
43 }; |
|
44 |
|
45 // Set ENDIAN to the platform's endianness. |
|
46 ENDIAN = 0; // try little-endian first |
|
47 if (diff(2, 4) === 0x100000) // exact wrong answer we'll get on a big-endian platform |
|
48 ENDIAN = 1; |
|
49 assertEq(diff(2,4), 0x10000000000000); |
|
50 assertEq(diff(0, Number.MIN_VALUE), 1); |
|
51 assertEq(diff(1, ONE_PLUS_EPSILON), 1); |
|
52 assertEq(diff(1, ONE_MINUS_EPSILON), 1); |
|
53 |
|
54 var assertNear = function assertNear(a, b, tolerance=1) { |
|
55 if (!Number.isFinite(b)) { |
|
56 fail("second argument to assertNear (expected value) must be a finite number"); |
|
57 } else if (Number.isNaN(a)) { |
|
58 fail("got NaN, expected a number near " + b); |
|
59 } else if (!Number.isFinite(a)) { |
|
60 if (b * Math.sign(a) < Number.MAX_VALUE) |
|
61 fail("got " + a + ", expected a number near " + b); |
|
62 } else { |
|
63 // When the two arguments do not have the same sign bit, diff() |
|
64 // returns some huge number. So if b is positive or negative 0, |
|
65 // make target the zero that has the same sign bit as a. |
|
66 var target = b === 0 ? a * 0 : b; |
|
67 var err = diff(a, target); |
|
68 if (err > tolerance) { |
|
69 fail("got " + a + ", expected a number near " + b + |
|
70 " (relative error: " + err + ")"); |
|
71 } |
|
72 } |
|
73 }; |
|
74 } |