1. 现象: 为什么读到的温度计数据小数位有那么多?
刚到了一个温湿度传感器,我就把他接入到八哥开关小帮手上面。 数据是通过Modbus-RTU的协议传输到服务器上,是一个整型 int16AB 编码,刚好是10倍的关系, 计算公式为 value = 0.1 * read_value。 计算出来的数据很奇怪,18.9000000000002,后面多出来了好多的小数点,用眼睛看就知道有问题了。 我们希望的是得到 18.9,但计算机给出了一个带有微小“尾巴”的数字。这并不是 Python 的 bug,而是计算机底层存储浮点数方式导致的物理限制。
2. 原理:二进制的世界没有 0.1
计算机使用 IEEE 754 标准 的二进制浮点数来存储小数。
在十进制中, 是无限循环小数 ();同理,在二进制中, (即 ) 也是一个无限循环小数:
因为计算机的存储空间(53位有效数字)是有限的,它必须在某一位进行截断。这种截断导致了微小的精度丢失,就像把 截断为 一样,结果虽然极其接近,但在数学上并不完全相等。
3. 采用 decimal 金融级精确计算来解决
Decimal 模块模拟了人类手算的方式,避免了二进制转换误差,这样我们算出来的数据就不会出现那么多的小数位了。
Python
from decimal import Decimal
# 核心原则:必须以 '字符串' 形式初始化
a = Decimal('189')
b = Decimal('0.1')
print(a * b)
# 输出: 18.9 (类型为 Decimal)
警告:如果写成
Decimal(0.1),由于括号内的 0.1 已经是浮点数,误差会被带入Decimal中,导致前功尽弃。
4. 终于没有那么多的小数位了
经过 Decimal 的处理,终于可以计算出正确的数字了,没有那么多的小数位。