Python中浮点数的底层数据结构

6 投票
6 回答
1728 浏览
提问于 2025-04-16 21:48

我有个关于Python中浮点数(和精度)底层数据结构的问题:

>>> b = 1.4 + 2.3
>>> b
3.6999999999999997

>>> c = 3.7
>>> c
3.7000000000000002

>>> print b, c
3.7  3.7

>>> b == c
False

看起来b和c的值是依赖于机器的,它们是最接近目标值的数字,但并不完全相同。我之前听说过,我们通过'Print'得到的是“正确”的数字,还有人告诉我这是因为打印的结果“撒谎”,而Python选择告诉我们真相,也就是显示它们实际存储的内容。

我的问题是:

1. 怎么“撒谎”?比如在一个函数中,我们接收两个值并判断它们是否相同,如果小数点后的精度(位数)不确定,我该如何做出最佳猜测?就像上面提到的b和c那样?有没有什么明确的算法可以做到这一点?我听说每种语言(C/C++)在涉及浮点计算时都会有这个问题,但它们是怎么“解决”这个问题的呢?

2. 为什么我们不能直接存储实际的数字,而是存储最接近的数字?这是一个限制,还是为了提高效率而做的权衡?

非常感谢
约翰

6 个回答

1

你在计算时得到不同的结果,是因为数字1.4和2.3并不是被准确表示的。当你把它们相加时,也会把它们的精度限制加在一起。

所有的浮点数都有一个有限的精度,而由于浮点数通常是用二进制(基数2)来表示的,而不是我们常用的十进制(基数10),所以即使是我们认为容易准确表示的数字,也会受到这些限制。

这种有限的精度在大多数计算中通常不会造成问题,因为它的精度对于大多数应用来说还是足够的。不过,在比较浮点数时,就必须考虑到这个有限的精度。

通常的做法是把这两个数字相减,然后检查它们的差值是否相对于这两个数字来说足够小。

比如说,如果:

abs(b - c) < abs(b) / 1000000000000

那么你可以认为它们是相等的。你想考虑多少位数字取决于浮点数的精度,也就是你使用的是单精度还是双精度数字,以及你为了得到这些数字所做的计算。随着每次计算,精度的限制会累积,所以你可能需要降低判断它们是否相等的标准。

在显示浮点数时,它会根据其精度进行四舍五入。例如,如果它能准确表示15位数字,可能会在显示之前四舍五入到13位数字。

浮点数是为了快速计算而设计的。还有其他数据类型,比如Decimal,可以精确存储一个数字。这些通常用于存储货币值。

2

你应该看看那篇著名的论文:

每个计算机科学家都应该知道的浮点运算知识

点击上面写着“CACHED”的链接,可以下载这篇论文的PDF格式。

7

关于你第一个问题的答案,可以看看下面这段来自Python源代码的简化代码:

#define PREC_REPR       17
#define PREC_STR        12

void PyFloat_AsString(char *buf, PyFloatObject *v) {
    format_float(buf, 100, v, PREC_STR);
}

void PyFloat_AsReprString(char *buf, PyFloatObject *v) {
    format_float(buf, 100, v, PREC_REPR);
}

简单来说,repr(float)会返回一个格式化为17位数字的字符串,而str(float)会返回一个格式化为12位数字的字符串。你可能猜到了,print使用的是str(),而在解释器中输入变量名时使用的是repr()。由于只有12位数字的精度,看起来你得到了“正确”的答案,但这只是因为你期望的值和实际值在12位数字内是一样的。

这里有一个简单的例子来说明这个区别:

>>> str(.1234567890123)
'0.123456789012'
>>> repr(.1234567890123)
'0.12345678901230001'

至于你的第二个问题,我建议你阅读Python教程的以下部分:浮点数运算:问题和限制

这归结为效率,当你用二进制存储十进制数时,能节省内存并加快浮点运算速度,但你需要处理精度不够的问题。

正如JBernardo在评论中指出的,这种行为在Python 2.7及以上版本中是不同的,下面的引用来自上述教程链接,使用0.1作为例子:

在Python 2.7之前和Python 3.1之前的版本中,Python将这个值四舍五入到17位有效数字,结果是‘0.10000000000000001’。而在当前版本中,Python显示的值是基于最短的十进制分数,这个分数可以正确地四舍五入回真实的二进制值,因此结果就是‘0.1’。

撰写回答