为什么float.\urepr\uuuuuuuuu返回的表示形式与等效的格式选项不同?

2024-05-11 11:09:22 发布

您现在位置:Python中文网/ 问答频道 /正文

为了了解repr(x)如何在CPython中为float工作,我检查了^{}的源代码:

buf = PyOS_double_to_string(PyFloat_AS_DOUBLE(v),
                            'r', 0,
                            Py_DTSF_ADD_DOT_0,
                            NULL);

这将使用格式代码'r'调用^{},格式代码似乎转换为格式代码'g',精度设置为17:

precision = 17;
format_code = 'g';

所以我希望repr(x)f'{x:.17g}'返回相同的表示。但事实似乎并非如此:

>>> repr(1.1)
'1.1'
>>> f'{1.1:.17g}'
'1.1000000000000001'
>>> 
>>> repr(1.225)
'1.225'
>>> f'{1.225:.17g}'
'1.2250000000000001'

我知道repr只需要返回尽可能多的数字就可以重建内存中表示的完全相同的对象,因此'1.1'显然足以返回1.1,但是我想知道这与(内部使用的).17g格式选项有什么不同(或者为什么不同)。你知道吗

(Python 3.7.3版)


Tags: to代码pystring源代码as格式float
1条回答
网友
1楼 · 发布于 2024-05-11 11:09:22

似乎您正在寻找一种回退方法:

/* The fallback code to use if _Py_dg_dtoa is not available. */

PyAPI_FUNC(char *) PyOS_double_to_string(double val,
                                         char format_code,
                                         int precision,
                                         int flags,
                                         int *type)
{
    char format[32];

制约回退方法的预处理器变量是PY_NO_SHORT_FLOAT_REPR。如果设置了dtoa,则不会将it will fail编译为:

/* if PY_NO_SHORT_FLOAT_REPR is defined, then don't even try to compile the following code */

这可能不是大多数现代设备的情况。这个问答解释了Python何时/为什么选择这两种方法:What causes Python's float_repr_style to use legacy?

现在在line 947有了可用的版本

/* _Py_dg_dtoa is available. */


static char *
format_float_short(double d, char format_code,
                   int mode, int precision,
                   int always_add_sign, int add_dot_0_if_integer,
                   int use_alt_formatting, const char * const *float_strings,
                   int *type)

在那里你可以看到gr有细微的差别(在注释中解释)

We used to convert at 1e17, but that gives odd-looking results for some values when a 16-digit 'shortest' repr is padded with bogus zeros.

case 'g':
    if (decpt <= -4 || decpt >
        (add_dot_0_if_integer ? precision-1 : precision))
        use_exp = 1;
    if (use_alt_formatting)
        vdigits_end = precision;
    break;
case 'r':
    /* convert to exponential format at 1e16.  We used to convert
       at 1e17, but that gives odd-looking results for some values
       when a 16-digit 'shortest' repr is padded with bogus zeros.
       For example, repr(2e16+8) would give 20000000000000010.0;
       the true value is 20000000000000008.0. */
    if (decpt <= -4 || decpt > 16)
        use_exp = 1;
    break;

似乎和你描述的行为相符。注意,"{:.16g}".format(1.225)产生1.225

相关问题 更多 >