f 为 float 时 repr(f)、str(f)、print(f) 的精度

问题描述 投票:0回答:1

如果我跑步:

>>> import math
>>> print(math.pi)
3.141592653589793

然后打印 pi 16 位数字,

但是,根据:

>>> import sys
>>> sys.float_info.dig 
15

我的精度是15位。

那么,我应该依赖该值的最后一位数字(即 π 的值确实是 3.141592653589793nnnnnn)。

python floating-point-precision
1个回答
20
投票

TL;博士

str(float)
repr(float)
的最后一位数字可能是“错误的”,因为小数表示似乎未正确舍入。

>>> 0.100000000000000040123456
0.10000000000000003

但是这个值仍然比

0.1000000000000000
(少了 1 位)更接近原始值。

math.pi
的情况下,pi 的十进制近似值为 3.141592653589793238463...,在 this 的情况下最后一位数字是正确的。

sys.float_info.dig
表示保证多少位小数位始终精确。


Python 3.1+(以及 2.7 中的

str(float)
)中
repr(float)
repr
的默认输出是最短字符串,转换为
float
时将返回原始值;如果有歧义,最后一位数字将四舍五入到最接近的值。浮点型提供约 15.9 位小数位的精度;但实际上需要 17 位小数位精度才能明确表示 53 位二进制浮点数,

例如

0.10000000000000004
介于
0x1.999999999999dp-4
0x1.999999999999cp-4
之间,但后者更接近;这 2 个有十进制扩展

0.10000000000000004718447854656915296800434589385986328125

0.100000000000000033306690738754696212708950042724609375

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

现在,当使用

str()
repr()
将它们转换回字符串时,会选择产生完全相同值的最短字符串;对于这 2 个值,它们分别是
0.10000000000000005
0.10000000000000003


IEEE-754中

double
的精度为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;
}

然后,对于

PyOS_double_to_string
模式(代表 repr),
'r'
会调用模式 0 的
_Py_dg_dtoa
(这是将双精度数转换为字符串的内部例程),或者使用
snprintf
调用
%17g
对于那些
_Py_dg_dtoa
不起作用的平台。

snprintf 的行为完全依赖于平台,但是如果使用

_Py_dg_dtoa
(据我了解,它应该在大多数机器上使用),它应该是可预测的。

_Py_dg_dtoa
模式0指定如下:

0 ==> 读入并舍入到最接近的值时产生 d 的最短字符串。

所以,这就是发生的情况 - 生成的字符串在读入时必须准确地再现

double
值,并且它必须是可能的最短表示形式,并且在将读入的多个十进制表示形式中,它将是最短的表示形式最接近二进制值。现在,这也可能意味着十进制扩展的最后一位与按该长度四舍五入的原始值不匹配,只是十进制表示形式尽可能接近原始二进制表示形式。因此YMMV。

© www.soinside.com 2019 - 2024. All rights reserved.