-
Notifications
You must be signed in to change notification settings - Fork 38
Expand file tree
/
Copy pathPrecision.js
More file actions
179 lines (157 loc) · 5.64 KB
/
Precision.js
File metadata and controls
179 lines (157 loc) · 5.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/**
* Precision
*/
let Precision = {
/**
* Standard epsilon, the maximum relative precision of IEEE 754 double-precision floating numbers (64 bit).
* According to the definition of Prof. Demmel and used in LAPACK and Scilab.
*/
doublePrecision: Math.pow(2, -53),
/**
* Standard epsilon, the maximum relative precision of IEEE 754 double-precision floating numbers (64 bit).
* According to the definition of Prof. Higham and used in the ISO C standard and MATLAB.
*/
positiveDoublePrecision: 2 * Math.pow(2, -53),
/**
* Value representing 10 * 2^(-53) = 1.11022302462516E-15
*/
defaultDoubleAccuracy: Math.pow(2, -53) * 10,
/**
* Converts a double to a 64-bit integer representation
*/
doubleToInt64Bits: function(value) {
let buffer = new ArrayBuffer(8);
let float64Array = new Float64Array(buffer);
let int64Array = new BigInt64Array(buffer);
float64Array[0] = value;
return int64Array[0];
},
/**
* Converts a 64-bit integer representation to a double
*/
int64BitsToDouble: function(bits) {
let buffer = new ArrayBuffer(8);
let int64Array = new BigInt64Array(buffer);
let float64Array = new Float64Array(buffer);
int64Array[0] = bits;
return float64Array[0];
},
/**
* Increments a floating point number to the next bigger number representable by the data type.
* @param {number} value - The value which needs to be incremented.
* @param {number} count - How many times the number should be incremented.
* @returns {number} The next larger floating point value.
*/
increment: function(value, count = 1) {
if (isNaN(value) || !isFinite(value) || count === 0) {
return value;
}
// Translate the bit pattern of the double to an integer
let intValue = this.doubleToInt64Bits(value);
if (intValue < 0) {
intValue -= BigInt(count);
} else {
intValue += BigInt(count);
}
// Note that BigInt(-9223372036854775808) has the same bit pattern as -0.0
if (intValue === BigInt(-9223372036854775808)) {
return 0;
}
return this.int64BitsToDouble(intValue);
},
/**
* Evaluates the minimum distance to the next distinguishable number near the argument value.
* @param {number} value - The value used to determine the minimum distance.
* @returns {number} Relative Epsilon (positive double or NaN).
*/
epsilonOf: function(value) {
if (isNaN(value) || !isFinite(value)) {
return NaN;
}
let signed64 = this.doubleToInt64Bits(value);
if (signed64 === 0n) {
signed64++;
return this.int64BitsToDouble(signed64) - value;
}
if (signed64 < 0n) {
signed64--;
return this.int64BitsToDouble(signed64) - value;
}
signed64--;
return value - this.int64BitsToDouble(signed64);
},
/**
* Evaluates the minimum distance to the next distinguishable number near the argument value.
* @param {number} value - The value used to determine the minimum distance.
* @returns {number} Relative Epsilon (positive double or NaN).
*/
positiveEpsilonOf: function(value) {
return 2 * this.epsilonOf(value);
},
/**
* @param {number} a
* @param {number} b
* @param {number} diff
* @param {number} maximumError
* @returns {boolean}
*/
almostEqualNormRelative: function(a, b, diff, maximumError) {
// If A or B are infinity (positive or negative) then
// only return true if they are exactly equal to each other -
// that is, if they are both infinities of the same sign.
if (!isFinite(a) || !isFinite(b)) {
return a === b;
}
// If A or B are a NAN, return false. NANs are equal to nothing,
// not even themselves.
if (isNaN(a) || isNaN(b)) {
return false;
}
// If one is almost zero, fall back to absolute equality
if (Math.abs(a) < this.doublePrecision || Math.abs(b) < this.doublePrecision) {
return Math.abs(diff) < maximumError;
}
if ((a === 0 && Math.abs(b) < maximumError) || (b === 0 && Math.abs(a) < maximumError)) {
return true;
}
return Math.abs(diff) < maximumError * Math.max(Math.abs(a), Math.abs(b));
},
/**
* @param {number} a
* @param {number} b
* @returns {boolean}
*/
almostEqualRelative: function(a, b) {
return this.almostEqualNormRelative(a, b, a - b, this.defaultDoubleAccuracy);
},
/**
* @param {number} a
* @param {number} b
* @returns {boolean}
*/
almostEqual: function(a, b) {
return this.almostEqualNorm(a, b, a - b, this.defaultDoubleAccuracy);
},
/**
* @param {number} a
* @param {number} b
* @param {number} diff
* @param {number} maximumAbsoluteError
* @returns {boolean}
*/
almostEqualNorm: function(a, b, diff, maximumAbsoluteError) {
// If A or B are infinity (positive or negative) then
// only return true if they are exactly equal to each other -
// that is, if they are both infinities of the same sign.
if (!isFinite(a) || !isFinite(b)) {
return a === b;
}
// If A or B are a NAN, return false. NANs are equal to nothing,
// not even themselves.
if (isNaN(a) || isNaN(b)) {
return false;
}
return Math.abs(diff) < maximumAbsoluteError;
}
};
export { Precision };