当f为浮点时,repr(f)、str(f)和print(f)的精度



如果我运行:

>>> 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-40x1.999999999999cp-4之间,但后者更接近;这2个具有十进制展开

0.10000000000000004718447854656915296800434589385986328125

0.100000000000000033306690738754696212708950042724609375

分别。显然,后者更接近,因此选择了二进制表示。

现在,当这些被转换回具有str()repr()的字符串时,会选择产生完全相同值的最短字符串;对于这两个值,它们分别是0.100000000000000050.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。

最新更新