Python默认round函数为何能解决双精度舍入误差问题?
我对Python的round函数或双精度浮点数的表示方式存在理解偏差。在对存在双精度舍入误差的数值进行舍入时,发现了一个令人困惑的行为:例如1.01046 + .00002的结果为1.0104799999999998,将其舍入到5位小数时,Python返回了预期结果1.01048。但我原本认为,之所以得到1.0104799999999998,是因为不存在精确等于1.01048的双精度浮点数。如果round函数接收并返回双精度类型数值,它为何能返回不存在的1.01048?
测试代码1:
print([1.01046 + .00002*i for i in range(6)])
输出:
[1.01046, 1.0104799999999998, 1.0105, 1.0105199999999999, 1.01054, 1.01056]
测试代码2:
print([round(1.01046 + .00002*i, 5) for i in range(6)])
输出:
[1.01046, 1.01048, 1.0105, 1.01052, 1.01054, 1.01056]
为什么Python无法精确表示1.01046 + .00002*1,只能给出近似值,却在对该近似值执行round操作后能返回1.01048?
核心原因解析
双精度浮点数的存储本质
1.01048确实无法用双精度浮点数精确存储,但你看到的1.01048只是Python对其内存中近似值的友好显示形式。双精度浮点数基于二进制存储,绝大多数十进制小数无法转换为有限长度的二进制小数,因此1.01048在内存中实际是一个非常接近它的二进制近似值,Python打印时会自动选择最简洁的十进制字符串来代表这个近似值。round函数的实际计算逻辑
round(1.0104799999999998, 5)的作用是找到最接近原数的、保留5位小数的十进制数对应的双精度近似值。1.0104799999999998本身就极接近1.01048的双精度近似值,round函数计算后会指向这个近似值,而Python输出时会将其简化显示为1.01048,而非它真实存储对应的冗长十进制表示。浮点数打印的优化规则
Python对浮点数打印做了优化:当一个浮点数可以对应多个十进制字符串时,它会选择最短且正确的那个。比如1.01048的双精度近似值,打印时不会显示其真实的冗长十进制形式,而是直接输出1.01048——因为这个字符串对应的双精度值,就是内存中实际存储的那个近似值。
内容的提问来源于stack exchange,提问作者AXensen

