You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

64位x86 Linux下Python双精度浮点数为何仅支持13位精度?

双精度浮点数精度疑问

我查阅了大量浮点数相关资料,但仍没搞清楚问题所在。64位x86机器上的Python3 CPython解释器将float存储为8字节(64位)的双精度类型,基于C double实现,通常遵循IEEE 754双精度二进制浮点数(binary64)标准,按维基百科说法,该标准应提供约16位十进制精度。

但在以下测试代码中,两个仅第16位十进制数不同的浮点数,在我的Python3.10(64位x86 Linux)环境中,无论是用float还是Decimal类型,都只能区分到13位小数,这是为什么?

测试代码

lat1 = -81.0016666666670072 # or float(-81.0016666666670072)
lat2 = -81.0016666666670062  # or float(-81.0016666666670062)
print("Out as string lat1=-81.0016666666670072 lat2= -81.0016666666670062")
print(f"Precision 16 lat1:.16f {lat1:.16f} lat2:.16f{lat2:.16f}")

# Lets see how it is store in hardware
print(f"Stored in HW as lat1.hex() {lat1.hex()} lat2.hex() {lat2.hex()}")
x = float.fromhex(lat1.hex())
y = float.fromhex(lat2.hex())
print(f"Reconstructed from Hex lat1:.16f {x:.16f} lat2:.16f{y:.16f}")

try:
    assert lat1 != lat2 
except:
    # Assert false - means Python is telling lat1 == lat2
    print(f"Fail, {lat1} and {lat2} are really different at precision 16")

# try with Decimal
from decimal import *
getcontext().prec = 16

try:
   assert Decimal(lat1).compare(Decimal(lat2))
except:
    # Assert false - means Python is telling both are same
    print(f"Fail, Decimal(lat1) {Decimal(lat1):.16f} and Decimal(lat2) {Decimal(lat2):.16f} are really different at precision 16")

print("Reducing precision to 14")
lat1 = -81.00166666666711
lat2 = -81.00166666666710
print(f"At precision 14-still equal lat1:.14f {lat1:.14f} lat2:.14f{lat2:.14f}")

print("Reducing precision to 13")
lat1 = -81.0016666666671
lat2 = -81.0016666666670
# Lets see string representation
print(f"At precision 13-Not equal lat1:.13f {lat1:.13f} lat2:.13f{lat2:.13f}")

try:
   assert lat1 == lat2
except:
    # Assert false - means Python is telling lat1 != lat2, which is correct
    print(f"Pass, {lat1} and {lat2} are different")

代码输出

Out as string lat1=-81.0016666666670072 lat2= -81.0016666666670062
Precision 16 lat1:.16f -81.0016666666670062 lat2:.16f-81.0016666666670062
Stored in HW as lat1.hex() -0x1.4401b4e81b500p+6 lat2.hex() -0x1.4401b4e81b500p+6
Reconstructed from Hex lat1:.16f -81.0016666666670062 lat2:.16f-81.0016666666670062
Fail, -81.001666666667 and -81.001666666667 are really different at precision 16
Fail, Decimal(lat1) -81.0016666666670062 and Decimal(lat2) -81.0016666666670062 are really different at precision 16
Reducing precision to 14
At precision 14-still equal lat1:.14f -81.00166666666711 lat2:.14f-81.00166666666711
Reducing precision to 13
At precision 13-Not equal lat1:.13f -81.0016666666671 lat2:.13f-81.0016666666670
Pass, -81.0016666666671 and -81.001666666667 are different

注:这些数值是地理位置数据,我知道不需要16位精度,但仍好奇为何只能获得13位小数精度。同时我也疑惑,为何无法通过代码获取操作系统/硬件支持的精度位数。


问题解答

1. 核心原因:有效数字位数≠小数位数

IEEE 754双精度的16位十进制精度指的是有效数字总数,不是小数部分的位数。你的测试值-81.0016666666670072整数部分占2位,所以小数部分能有效区分的位数实际是16-2=14位左右。更关键的是:大多数十进制小数无法精确转换为二进制小数,会被舍入到最接近的可表示二进制浮点数。

你给出的lat1和lat2差异仅为1e-16,远小于该数值附近的最小可分辨单位(ULP)——对于81左右的双精度浮点数,ULP约为1e-14,也就是说两个数的差异至少要达到这个量级才能被区分。因此这两个值会被舍入到同一个二进制浮点数,自然无法区分。

2. Decimal无法区分的原因

你用Decimal(lat1)转换时,lat1已经是被舍入后的二进制浮点数,转换为Decimal只是把这个丢失精度的数值转成十进制表示,无法还原原始差异。如果要保留原始十进制精度,应该直接用字符串初始化Decimal:Decimal("-81.0016666666670072"),而非先转float再转Decimal。

3. 如何获取硬件/系统的浮点数精度

操作系统和硬件的浮点数精度由IEEE 754标准固定,双精度就是52位尾数(对应约16位十进制有效数字)。你可以通过Python内置模块直接查看:

  • 执行import sys后,sys.float_info.epsilon是双精度的机器epsilon(约2.22e-16),代表1.0和下一个可表示浮点数的差值;
  • sys.float_info.mant_dig是尾数位数(52),十进制有效位数可通过log10(2**52)计算,约15.65,所以通常说16位有效数字。

4. 为何13位小数才能区分

最后一组测试中,lat1和lat2的差异是1e-13,大于该数值附近的ULP(~1e-14),因此能被双精度浮点数区分。而14位小数的差异1e-14刚好等于ULP,此时会受舍入规则影响,可能被舍入到同一个值,所以无法区分。

内容的提问来源于stack exchange,提问作者Alex Punnen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 02:07:04