本文最后更新于:2024年1月14日 晚上

计算机在处理浮点数时会用二进制表示,遇到无法用二进制精确表示的十进制浮点数时便会根据精确度位数进行截断,Python 也不例外。

Python 精度

python 默认使用的是 double 精度, 浮点数在计算机中都是以二进制保存,当有无法精确表示的二进制数字时便会产生截断, 这就导致了在有限精度下,电脑为自己把精度范围外的小数“掐掉”,导致结果不准确。

可以随时在 Python 环境下测试:

1
2
3
4
0.1+0.2

-->
0.30000000000000004

也就是说,如果你使用很精确的浮点数字计算的结果作为一个逻辑表达式时,可能会发生问题:

1
2
3
4
0.1 + 0.2 == 0.3

-->
False

问题原理

double 用 64 个bit 位表示数据

有效精度位数是 52 位,那么当表示的小数用52bit 无法精确表示时便会截断

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import numpy as np
import struct


def binary_add(a, b, pre_num=64):
int_a = np.floor(a).astype('int64')
int_b = np.floor(b).astype('int64')
int_sum = int_a + int_b

double_a = ""
double_b = ""

r_a = a - int_a
r_b = b - int_b

temp_a = r_a
temp_b = r_b

def up(num):
t = num * 2
if t >= 1:
x = 1
t -= 1
else:
x = 0
return t, x

for index in range(pre_num):
temp_a, bin_a = up(temp_a)
temp_b, bin_b = up(temp_b)
double_a = double_a + str(bin_a)
double_b = double_b + str(bin_b)

print(double_a)
print(double_b)

sum2 = int(double_a, 2) + int(double_b, 2)
sum_bin = bin(sum2)[2:]
print(sum_bin)

res_p = sum2 / 2 ** 64
return res_p + int_sum

res = binary_add(0.1, 0.2)
print(res)
pass

-->
0001100110011001100110011001100110011001100110011001101000000000
0011001100110011001100110011001100110011001100110011010000000000
0100110011001100110011001100110011001100110011001100111000000000
0.30000000000000004
  • 输出信息第一行为 0.1 的小数部分二进制表示,可以说:
    $$
    0.1\approx(0.0001100110011001100110011001100110011001100110011001101000000000)_2
    $$

    事实上 0.1 的二进制表示是一个以 1100 为循环体的无限循环小数,到有效位 53 位时被截断,之后的数据变为了全零

  • 同理,第二行有:
    $$
    0.2\approx(0.0011001100110011001100110011001100110011001100110011010000000000)_2
    $$

    本质上就是 0.1 左移一位而已,也是 1100 的无限循环小数,在第 53 位被截断

  • 二者变成整数相加后得到 :
    $$
    100110011001100110011001100110011001100110011001100111000000000
    $$

  • 该数据除以 $2^{64}$ 得到 $0.1+0.2$ 的结果,就是 $0.30000000000000004$

以上流程基本就是 Python 内部计算 $0.1+0.2$ 时的过程,其余语言也一样,这是由无限循环小数难以精确表示导致的。

解决方案

如果有需要更高精度计算的需求,可以继续提升有效 bit 位数。

如果仍然无法达到精度要求,可以使用 Python decimal 包实现。

参考资料



文章链接:
https://www.zywvvd.com/notes/coding/python/python-precision/python-precision/


“觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭”

微信二维码

微信支付

支付宝二维码

支付宝支付

Python 浮点数精度
https://www.zywvvd.com/notes/coding/python/python-precision/python-precision/
作者
Yiwei Zhang
发布于
2023年4月25日
许可协议