关于Number.isSafeInteger运算结果不符合预期的技术疑问
先给结论:这不是JavaScript的bug,是它采用IEEE 754双精度浮点数标准带来的必然现象。
先搞懂Number.MAX_SAFE_INTEGER
它的值是2^53 - 1(即9007199254740991),这是JavaScript能精确表示的最大整数。超过这个值的整数,因为双精度浮点数的尾数只有53位(含隐含位),无法被精准存储——相邻的多个整数会被映射到同一个浮点数上。
逐个拆解你的代码例子
console.log( Number.isSafeInteger( Number.MAX_SAFE_INTEGER + 1) ) // false
这很合理:MAX_SAFE_INTEGER + 1已经超出精确表示范围,这个数无法被精准存储,所以是不安全整数。console.log( Number.isSafeInteger( (Number.MAX_SAFE_INTEGER + 1) - 1 ) ) // true
关键在于:MAX_SAFE_INTEGER + 1存储时是一个无法精确对应整数的浮点数,但当你减1时,它会被舍入回MAX_SAFE_INTEGER——这个数本身是安全的,所以isSafeInteger返回true。console.log( Number.isSafeInteger( (Number.MAX_SAFE_INTEGER + 2) - 1 ) ) // true
超过MAX_SAFE_INTEGER后,每2个整数会共享一个浮点数表示,所以MAX_SAFE_INTEGER + 2和MAX_SAFE_INTEGER + 1存储的是同一个值,减1后同样得到MAX_SAFE_INTEGER,自然返回true。console.log( (Number.MAX_SAFE_INTEGER + 2) - 1 === (Number.MAX_SAFE_INTEGER + 1) - 1 ) // true
两者最终都被计算为MAX_SAFE_INTEGER,所以结果相等。
纠正你的误解
Number.isSafeInteger的逻辑是只判断传入的最终数值是否为安全整数,不管这个数值的运算过程是否涉及不安全数。哪怕运算中用过不安全的数,只要最终结果是能精确表示的安全整数,它就返回true。你的“运算不安全则结果不安全”的期望,不符合这个API的设计逻辑。
内容的提问来源于stack exchange,提问作者QuarterOnion

