为什么 float.__repr__ return 与等效格式选项相比有不同的表示形式?
Why does float.__repr__ return a different representation compared to the equivalent formatting option?
为了了解 repr(x)
如何在 CPython 中用于浮点数,我检查了 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}'
到 return 相同的表示。然而,情况似乎并非如此:
>>> repr(1.1)
'1.1'
>>> f'{1.1:.17g}'
'1.1000000000000001'
>>>
>>> repr(1.225)
'1.225'
>>> f'{1.225:.17g}'
'1.2250000000000001'
我知道 repr
只需要 return 重建与内存中表示的完全相同的对象所需的数字,因此 '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 */
大多数现代设置可能不是这种情况。 此问答解释了 when/why Python 选择任一方法:
现在 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)
你可以看到 g
和 r
有细微的差别(在评论中解释)
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
为了了解 repr(x)
如何在 CPython 中用于浮点数,我检查了 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}'
到 return 相同的表示。然而,情况似乎并非如此:
>>> repr(1.1)
'1.1'
>>> f'{1.1:.17g}'
'1.1000000000000001'
>>>
>>> repr(1.225)
'1.225'
>>> f'{1.225:.17g}'
'1.2250000000000001'
我知道 repr
只需要 return 重建与内存中表示的完全相同的对象所需的数字,因此 '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 */
大多数现代设置可能不是这种情况。 此问答解释了 when/why Python 选择任一方法:
现在 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)
你可以看到 g
和 r
有细微的差别(在评论中解释)
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