python - 与等效的格式化选项相比,为什么 float.__repr__ 返回不同的表示?

标签 python python-3.x floating-point cpython

看如何repr(x)适用于 CPython 中的 float,我检查了 float_repr 的源代码:

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

这叫 PyOS_double_to_string 带格式代码 '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)

最佳答案

似乎您正在查看回退方法:

/* 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 可用的版本
/* _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) yield 1.225

关于python - 与等效的格式化选项相比,为什么 float.__repr__ 返回不同的表示?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57845006/

相关文章:

python Pandas : 'numpy.ndarray' object has no attribute 'apply'

python - 如何使用静态方法初始化静态变量?

python - 如何减少Python3请求中连接超时的等待?

python - Python 3 中的密码算术难题通用解决方案

python - 模块未找到错误 : No module named 'sklearn.cross_validation' ??如何修复它?

ruby floats 根据顺序加起来不同的值

Python 3 - 如何更新字典以在一个键中包含多个值?

python - react 编辑,只编辑discord.py中最新的命令

regex - 庆典 + sed : trim trailing zeroes off a floating point number?

python - 值错误 : could not convert string to float: id