如果我运行:
>>> import math
>>> print(math.pi)
3.141592653589793
然后用16位数字打印pi,
然而,根据:
>>> import sys
>>> sys.float_info.dig
15
我的精度是15位数。
那么,我应该依赖于该值的最后一位吗(即π的值确实是3.141592653589793nnnnnn)。
TL;DR
str(float)
或repr(float)
的最后一位可能是"错误的",因为十进制表示似乎没有正确取整。
>>> 0.100000000000000040123456
0.10000000000000003
但这个值仍然比0.1000000000000000
(少1位)更接近原始值
在math.pi
的情况下,π的十进制近似值为3.141592653589793238463…,在这种情况下,最后一位数字是正确的。
sys.float_info.dig
告诉保证有多少位小数总是精确。
Python 3.1+中str(float)
和repr(float)
的默认输出(repr
为2.7)是转换为float
时返回原始值的最短字符串;如果有歧义,最后一位数字四舍五入到最接近的值。浮点提供约15.9位小数精度;但实际上需要高达17个十进制数字的精度来明确地表示53个二进制数字,
例如,0.10000000000000004
位于0x1.999999999999dp-4
和0x1.999999999999cp-4
之间,但后者更接近;这2个具有十进制展开
0.10000000000000004718447854656915296800434589385986328125
和
0.100000000000000033306690738754696212708950042724609375
分别。显然,后者更接近,因此选择了二进制表示。
现在,当这些被转换回具有str()
或repr()
的字符串时,会选择产生完全相同值的最短字符串;对于这两个值,它们分别是0.10000000000000005
和0.10000000000000003
IEEE-754中CCD_ 17的精度为53位二进制数字;在十进制中,你可以通过取2^53的基于10的对数来计算精度,
>>> math.log(2 ** 53, 10)
15.954589770191001
意味着几乎16位精度。float_info
的精度告诉你总是可以期望有多少是可展示的,这个数字是15,因为有些数字有16个十进制数字是无法区分的。
然而,这并不是故事的全部。Python 3.2+内部发生的情况是,float.__str__
和float.__repr__
最终调用相同的C方法float_repr
:
float_repr(PyFloatObject *v)
{
PyObject *result;
char *buf;
buf = PyOS_double_to_string(PyFloat_AS_DOUBLE(v),
'r', 0,
Py_DTSF_ADD_DOT_0,
NULL);
if (!buf)
return PyErr_NoMemory();
result = _PyUnicode_FromASCII(buf, strlen(buf));
PyMem_Free(buf);
return result;
}
然后,对于'r'
模式(代表repr),PyOS_double_to_string
调用模式为0的_Py_dg_dtoa
(这是一个将double转换为字符串的内部例程),或者对于_Py_dg_dtoa
无法工作的平台,调用snprintf
和%17g
。
snprintf的行为完全依赖于平台,但如果使用_Py_dg_dtoa
(据我所知,它应该在大多数机器上使用),它应该是可预测的。
_Py_dg_dtoa
模式0指定如下:
0==>读取时产生d的最短字符串,并四舍五入到最接近的值。
因此,这就是发生的事情——当读入时,生成的字符串必须准确地再现double
值,并且它必须是尽可能短的表示形式,并且在将要读入的多个十进制表示形式中,它将是最接近二进制值的表示形式。现在,这也可能意味着小数展开的最后一位与按该长度取整的原始值不匹配,只是小数表示尽可能接近原始二进制表示。因此YMMV。