为何Double.greatestFiniteMagnitude与自身减1相等?Swift浮点精度疑问
为什么最大浮点数减1后仍等于自身?
先还原你在Swift REPL里的操作:
$ swift Welcome to Apple Swift version 3.1 (swiftlang-802.0.53 clang-802.0.42). Type :help for assistance. 1> Double.greatestFiniteMagnitude == (Double.greatestFiniteMagnitude - Double(1)) $R0: Bool = true
你提到Float32也存在同样情况,这其实是二进制浮点数的固有特性,和你之前了解的“小数点后精度误差”是同一原理的不同表现,只是场景从小数精度变成了大数的整数精度。
核心原因:浮点数的精度单位(ULP)随数值增大而变大
二进制浮点数(比如Double、Float)是通过「符号位 + 指数位 + 尾数位」的方式存储的:
- 指数位决定了数值的量级
- 尾数位决定了数值的精度(相当于有效数字)
当数值大到像Double.greatestFiniteMagnitude这种级别时,指数位已经拉到了最大值,此时尾数位能表示的**最小精度单位(ULP,Unit in the Last Place)**会变得非常大——大到远超过1。
举个具体的例子:
- Double的最大有限值是
1.7976931348623157e+308 - 此时它的ULP(相邻两个可表示的Double值之间的差)是
~1.99584e+292,这个数值比1大得多得多
当你用这个最大数减去1时,这个结果根本不在Double能精确表示的数值范围内,根据浮点数的默认舍入规则(向最近的可表示值舍入),这个“减1”的操作会直接被舍回原来的最大有限值,所以等式Double.greatestFiniteMagnitude == (Double.greatestFiniteMagnitude - 1)会返回true。
Float32(Swift里的Float)也是一样的道理:它的最大有限值的ULP同样远大于1,所以减1之后无法被精确区分,结果自然和原值相等。
补充:和“小数点后误差”的区别
你之前看到的“小数点后数值不准确”,是当数值量级较小时,尾数位的精度不足以覆盖所有小数位;而这次的情况是数值量级极大,尾数位的精度连整数1都无法覆盖了——简单来说就是,当数大到一定程度,浮点数已经“分辨不出”1和0的区别了。
内容的提问来源于stack exchange,提问作者Max Chuquimia
相关产品推荐
相关产品推荐

