情景: 0.1永远无法用以2为基底的小数,他是一个无限循环小数 0.0001100110011001100110011001100110011001100110011… 你只能得到一个近似值 比较有意思的一个点: 1/10 = 0.1 貌似是存储的精确值,但是实际上只是存储的最接近0.1的计算机可表示的二进制分数 于此同时,有许多额十进制数共享该最接近的二进制分数 比如: 0.1 、 0.10000000000000001 、 0.1000000000000000055511151231257827021181583404541015625 全都近似于 3602879701896397 / 2 ** 55 如果想要美观的输出,可以通过字符串格式化限定输出的位数

还有一个有趣的现象 0.1 + 0.1 + 0.1 == 0.3为False 这个原理就是上述 但是捏 如果一定要进行不精确表达,可以使用math.isclose()

BUT!

正如那篇文章的结尾所言,“对此问题并无简单的答案。” 但是也不必过于担心浮点数的问题! Python 浮点运算中的错误是从浮点运算硬件继承而来,而在大多数机器上每次浮点运算的误差不超过 2**53 分之一。 这对大多数任务来说都已足够,但你确实需要记住它并非十进制算术,且每次浮点运算都可能会导致新的舍入错误。

而如果想要进行精确的十进制模块的计算,尝试使用decimal模块或者fractions模块 还有很多模块,但是我懒得赘述了 自己看看吧

15.1.表示性错误

定义:指的是某些十进制小数无法以二进制精确表示这一事实造成的失误本小节将详细解释 “0.1” 的例子,并说明你可以怎样亲自对此类情况进行精确分析。 假定前提是已基本熟悉二进制浮点表示法。

表示性错误 是指某些(其实是大多数)十进制小数无法以二进制(以 2 为基数的计数制)精确表示这一事实造成的错误。 这就是为什么 Python(或者 Perl、C、C++、Java、Fortran 以及许多其他语言)经常不会显示你所期待的精确十进制数值的主要原因。

为什么会这样? 1/10 是无法用二进制小数精确表示的。 至少从 2000 年起,几乎所有机器都使用 IEEE 754 二进制浮点运算标准,而几乎所有系统平台都将 Python 浮点数映射为 IEEE 754 binary64 “双精度” 值。 IEEE 754 binary64 值包含 53 位精度,因此在输入时计算机会尽量将 0.1 转换为以 J/2**N 形式所能表示的最接近的小数,其中 J 为恰好包含 53 比特位的整数。 重新将

1
1 / 10 ~= J / (2**N)

写为

1
J ~= 2**N / 10

并且由于 J 恰好有 53 位 (即 >= 2**52 但 < 2**53),N 的最佳值为 56:

1
2
2**52 <=  2**56 // 10  < 2**53
True

也就是说,56 是唯一能使 J 恰好有 53 位的 N 值。 这样 J 可能的最佳就是舍入之后的商:

1
2
3
q, r = divmod(2**56, 10)
r
6

由于余数超过 10 的一半,所以最佳近似值可通过向上舍入获得:

1
2
q+1
7205759403792794

因此在 IEEE 754 双精度下可能达到的 1/10 的最佳近似值为:

1
7205759403792794 / 2 ** 56

分子和分母都除以二则结果小数为:

1
3602879701896397 / 2 ** 55

请注意由于我们做了向上舍入,这个结果实际上略大于 1/10;如果我们没有向上舍入,则商将会略小于 1/10。 但无论如何它都不会是 精确的 1/10!

因此计算机永远不会 “看到” 1/10: 它实际看到的就是上面所给出的小数,即它能达到的最佳 IEEE 754 双精度近似值:

1
2
0.1 * 2 ** 55
3602879701896397.0

如果我们将该小数乘以 10**55,我们可以看到该值输出 55 个十进制数位:

1
2
3602879701896397 * 10 ** 55 // 2 ** 55
1000000000000000055511151231257827021181583404541015625

这意味着存储在计算机中的确切数字等于十进制数值 0.1000000000000000055511151231257827021181583404541015625。 许多语言(包括较旧版本的 Python)都不会显示这个完整的十进制数值,而是将结果舍入到 17 位有效数字:

1
2
format(0.1, '.17f')
'0.10000000000000001'

fractions 和 decimal 模块使得这样的计算更为容易:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from decimal import Decimal
from fractions import Fraction

Fraction.from_float(0.1)
Fraction(3602879701896397, 36028797018963968)

(0.1).as_integer_ratio()
(3602879701896397, 36028797018963968)

Decimal.from_float(0.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')

format(Decimal.from_float(0.1), '.17')
'0.10000000000000001'

自己看看吧,讲得还是挺清楚的