Java中MAX_SAFE_INTEGER与double精度异常问题咨询
这个问题核心在于IEEE 754双精度浮点数的有效精度限制和就近舍入规则,咱们掰开揉碎了说:
先明确基础规则
double类型是64位的IEEE 754浮点数,其中52位是尾数,再加上一个隐含的最高位1,总共提供53位二进制有效精度——转换成十进制的话,大概是15到17位有效数字(注意是整个数的有效数字,不是整数或小数部分单独算)。
你提到的2^53-1是能精确表示的最大整数,这没错,但带小数的数就不一样了:绝大多数十进制小数无法转换成有限长度的二进制小数,所以必须舍入到最接近的、可被double表示的值。
逐个分析你的例子
第一个案例:9007.199254740991
咱们数一下这个数的有效数字:9 0 0 7 . 1 9 9 2 5 4 7 4 0 9 9 1,一共16位,刚好踩在double的有效精度边缘。
当把这个十进制数转成二进制时,它无法用53位尾数精确表示。此时会触发IEEE 754的就近舍入规则:计算这个数与两个相邻可表示double值的距离,舍入到更近的那个。
实际计算后,9007.199254740991离9007.199254740992的距离比离前一个可表示值更近,所以最终存储的就是后者,输出自然就是它。
第二个案例:9007199.254740991
同样数有效数字:9 0 0 7 1 9 9 . 2 5 4 7 4 0 9 9 1,也是16位。
但这次,这个数离9007199.25474099的距离比离下一个可表示的double值更近,所以舍入后就丢掉了最后一位1,输出的就是9007199.25474099。
补充:舍入的细节
如果遇到某个数刚好在两个可表示值的正中间,double会用银行家舍入(就近舍入到偶数)——也就是选择尾数最后一位是偶数的那个值。不过你的两个例子里都是距离不等的情况,所以直接舍入到更近的那个。
你可以用Double.doubleToLongBits()方法把这两个double值转成二进制位,查看具体的尾数和指数,就能更直观地理解为什么会得到这样的结果。
内容的提问来源于stack exchange,提问作者Theboy

