惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Vercel News
Vercel News
博客园_首页
Y
Y Combinator Blog
美团技术团队
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
阮一峰的网络日志
阮一峰的网络日志
aimingoo的专栏
aimingoo的专栏
H
Hackread – Cybersecurity News, Data Breaches, AI and More
MyScale Blog
MyScale Blog
GbyAI
GbyAI
人人都是产品经理
人人都是产品经理
T
Tailwind CSS Blog
MongoDB | Blog
MongoDB | Blog
D
DataBreaches.Net
博客园 - Franky
Engineering at Meta
Engineering at Meta
量子位
The GitHub Blog
The GitHub Blog
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
N
Netflix TechBlog - Medium

又见苍岚

COLMAP PatchMatch Stereo 算法详解 事件驱动的状态机框架:从理论到工程实践 Git 在国内网络环境下无法 Push 的排查与修复 —— 配置 Clash 代理 分段五次多项式插值原理详解 路径插值方法深度对比研究 Claude Code 使用指南 OpenClaw 记忆管理与技能创建指南 CBS(Conflict-Based Search)算法详解 A* 算法及其变种详解 OpenClaw 配置多 Agents Windows Powershell 无法加载文件,因为在此系统上禁止运行脚本问题的解决方案 MaxClaw 安装流程 大模型 AI 名词介绍 AList 网盘聚合工具简介 Protobuf 简介与测试 Claude Code 简介以及 GLM 4.7 模型接入 Github 歌词下载工具 163MusicLyrics Python __getattr__ 懒加载 Python TypedDict 机器人仿真平台 Gazebo 安装记录 机器人仿真平台 Gazebo 简介 多机器人路径规划问题(Multi-Agent Path Finding, MAPF)简介 Python exifread 读取修改过的 jpeg 信息错误问题修复 3D 坐标系变换的理解 3D 旋转矩阵基本概念 MongoDB Compass 介绍 Python 环境管理工具 uv Flutter 开发指南 Snipaste 安装下载与黑屏问题解决方案 全局路径规划算法记录
Python 浮点数精度
Yiwei Zhang · 2023-04-25 · via 又见苍岚

计算机在处理浮点数时会用二进制表示,遇到无法用二进制精确表示的十进制浮点数时便会根据精确度位数进行截断,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/