Python中浮点数的底层数据结构
我有个关于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.4和2.3并不是被准确表示的。当你把它们相加时,也会把它们的精度限制加在一起。
所有的浮点数都有一个有限的精度,而由于浮点数通常是用二进制(基数2)来表示的,而不是我们常用的十进制(基数10),所以即使是我们认为容易准确表示的数字,也会受到这些限制。
这种有限的精度在大多数计算中通常不会造成问题,因为它的精度对于大多数应用来说还是足够的。不过,在比较浮点数时,就必须考虑到这个有限的精度。
通常的做法是把这两个数字相减,然后检查它们的差值是否相对于这两个数字来说足够小。
比如说,如果:
abs(b - c) < abs(b) / 1000000000000
那么你可以认为它们是相等的。你想考虑多少位数字取决于浮点数的精度,也就是你使用的是单精度还是双精度数字,以及你为了得到这些数字所做的计算。随着每次计算,精度的限制会累积,所以你可能需要降低判断它们是否相等的标准。
在显示浮点数时,它会根据其精度进行四舍五入。例如,如果它能准确表示15位数字,可能会在显示之前四舍五入到13位数字。
浮点数是为了快速计算而设计的。还有其他数据类型,比如Decimal,可以精确存储一个数字。这些通常用于存储货币值。
关于你第一个问题的答案,可以看看下面这段来自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’。