To a precision of 1 decimal digits, the maximum number you can work with is 562949953421311
.
To a precision of 2 decimal digits, it's 70368744177663
.
Interestingly, the first number is equal to:
(Number.MAX_SAFE_INTEGER + 1) / 16 - 1
And the second number is equal to:
(Number.MAX_SAFE_INTEGER + 1) / 128 - 1
What we're looking for, is the maximum safe number to support a precision of d
digits after the decimal point.
By "support" I mean "can reliably do basic arithmetic".
For example, we know that Number.MAX_SAFE_INTEGER (aka 2**53-1)
is not safe, because basic arithmetic is broken:
Number.MAX_SAFE_INTEGER - 0.1 === Number.MAX_SAFE_INTEGER
>>> true // unsafe
And we know that 0 is safe, since:
0 + 0.1 === 0
>>> false // safe
BTW, 0
is reliable as far as 1e-323
(including):
0 + 1e-323 === 0
>>> false // safe
0 + 1e-324 === 0
>>> true // unsafe
I binary-searched between 0 and Number.MAX_SAFE_INTEGER
for the biggest number that answers that definition, and came up with these numbers.
Here's the code (pass any other number to findMaxSafeFloat()
at the end of snippet)
/**Returns whether basic arithmetic breaks between n and n+1, to a precision of `digits` after the decimal point*/
function isUnsafe(n, digits) {
// digits = 1 loops 10 times with 0.1 increases.
// digits = 2 means 100 steps of 0.01, and so on.
let prev = n;
for (let i = 10 ** -digits; i < 1; i += 10 ** -digits) {
if (n + i === prev) { // eg 10.2 === 10.1
return true;
}
prev = n + i;
}
return false;
}
/**Binary search between 0 and Number.MAX_SAFE_INTEGER (2**53 - 1) for the biggest number that is safe to the `digits` level of precision.
* digits=9 took ~30s, I wouldn't pass anything bigger.*/
function findMaxSafeFloat(digits, log = false) {
let n = Number.MAX_SAFE_INTEGER;
let lastSafe = 0;
let lastUnsafe = undefined;
while (true) {
if (log) {
console.table({
'': {
n,
'Relative to Number.MAX_SAFE_INTEGER': `(MAX + 1) / ${(Number.MAX_SAFE_INTEGER + 1) / (n + 1)} - 1`,
lastSafe,
lastUnsafe,
'lastUnsafe - lastSafe': lastUnsafe - lastSafe
}
});
}
if (isUnsafe(n, digits)) {
lastUnsafe = n;
} else { // safe
if (lastSafe + 1 === n) { // Closed in as far as possible
console.log(`
Max safe number to a precision of ${digits} digits after the decimal point: ${n}((MAX + 1) / ${(Number.MAX_SAFE_INTEGER + 1) / (n + 1)} - 1)
`);
return n;
} else {
lastSafe = n;
}
}
n = Math.round((lastSafe + lastUnsafe) / 2);
}
}
console.log(findMaxSafeFloat(1));
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…