TL;DR:您的示例实际上并没有看到最后一位数字。将有限二进制表示的尾数转换为十进制时,最后一位数字始终应该是0
或5
。
请查看cpython/pystrtod.c
中的评论:
char * PyOS_double_to_string(double val,
char format_code,
int precision,
int flags,
int *type)
{
char format[32];
Py_ssize_t bufsize;
char *buf;
int t, exp;
int upper = 0;
switch (format_code) {
case 'r':
if (precision != 0) {
PyErr_BadInternalCall();
return NULL;
}
precision = 17;
format_code = 'g';
break;
}
维基百科 证实:
53位有效数字的精度能提供15到17位有效十进制数字的精度 (2-53 ≈ 1.11 × 10-16)。如果将一个最多有15位有效数字的十进制字符串转换为IEEE 754双精度表示,然后将其转换为具有相同位数的十进制字符串,则最终结果应与原始字符串匹配。如果将IEEE 754双精度数转换为至少有17个有效数字的十进制字符串,然后将其转换回双精度表示,最终结果必须与原始数字匹配。
因此,当我们使用 str
(或 repr
)时,我们只以十进制17位有效数字的形式表示。这意味着一些浮点数将被截断。实际上,要获得精确的表示,您需要53位有效数字的精度!您可以如下验证:
>>> counts = Counter(
... len(f"{random():.99f}".lstrip("0.").rstrip("0"))
... for _ in range(1000000)
... )
>>> counts
Counter({53: 449833,
52: 270000,
51: 139796,
50: 70341,
49: 35030,
48: 17507,
47: 8610,
46: 4405,
45: 2231,
44: 1120,
43: 583,
42: 272,
41: 155,
40: 60,
39: 25,
38: 13,
37: 6,
36: 5,
35: 4,
34: 3,
32: 1})
>>> max(counts)
53
现在使用最高精度,这是找到“最后一位数字”的正确方法:
>>> counts = Counter(
... int(f"{random():.53f}".lstrip("0.").rstrip("0")[-1])
... for _ in range(1000000)
... )
>>> counts
Counter({5: 1000000})
因此,最后一位数字始终为
5
。(或在非常罕见的情况下为
0
。)这是有道理的,因为:
2**0 == 1.0
2**-1 == 0.5
2**-2 == 0.25
2**-3 == 0.125
2**-4 == 0.0625
2**-5 == 0.03125
2**-6 == 0.015625
2**-7 == 0.0078125
2**-8 == 0.00390625
2**-9 == 0.001953125
...
2**-k == 0.[k-1 digits]5
所有尾数都是这些系数的某些部分和。
注意:正如用户2357112指出的那样,要查看正确的实现,请参阅 PyOS_double_to_string
和 format_float_short
,但我将保留当前的内容,因为它更具教育意义。
str
将其转换为十进制,这可能会导致问题。例如,一个1位浮点数尾数b0 -> 1.0
和b1 -> 1.5
。 "最后一位数字"始终为0
或5
。 - Mateen Ulhaqrandom.randrange(10)
更加明显。random.randint
(在幕后调用random.randrange
)是random
模块的后期添加,供那些不理解 Python 中范围工作原理的人使用。 ;) - PM 2Ringrandint
接口是一个错误之后,randrange
才出现。 - user2357112random.randrange(10)
保证每个数字出现的机会相等,而int(10*random.random())
可能具有微妙的不同机会。 - Marius Gedminas