提问人:Jagerber48 提问时间:7/24/2023 最后编辑:paxdiabloJagerber48 更新时间:7/25/2023 访问量:115
映射到同一双精度浮点数的两个 16 位十进制数字是多少?
What are two numbers of 16 decimal digits precision that map to the same double-precision float?
问:
据说,在双精度浮点数所涵盖的范围内,任何以 15 位精度表示的十进制数都可以用特定的浮点数唯一标识。但是,如果我们包含更多精度的数字(即 16),我们可能会看到两个或多个十进制数对应于同一个浮点数。有关详细信息,请参阅 https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/。
有人可以为我提供两个十进制精度为 16 位的数字映射到同一个双精度浮点数的示例吗?
答:
发布这个问题后不久,我在链接文章的评论中找到了答案。 16 位小数
8.000 000 000 000 001
和
8.000 000 000 000 002
映射到同一个浮点数。
在 Python 中,我看到
>>> float(8.000000000000001) == float(8.000000000000002)
True
评论
nextafter
可能有用的知识(在 Python 的模块中)。此外,使用该模块查看完整的十进制扩展有助于查看给定浮点数所表示的实际值math
decimal
在双精度中,您有大约 52 个二进制精度(这转换为大约 15 个十进制数字),可以上下缩放指数“曲线”(1)。这意味着,只有当 2 的所有这些幂都在该距离内(在曲线上)时,由二的幂组成的数字才可微分。
因此,几乎可以肯定的是,并且会变成相同的数字,因为第二个数字的两个项之间的距离在曲线上相差 60。250
250 + 2-10
运行以下 C 代码可确认这一点:
#include <math.h>
#include <stdio.h>
int main(void) {
double d1 = pow(2, 50);
double d2 = d1 + pow(2, -10);
if (d1 == d2) puts("Same");
return 0;
}
就精度为 16 位十进制位的两个数字而言,这也是可行的,就像大约 .这还不完全达到,所以你需要寻找的是大部分是零的数字,但在尾数的顶部和底部有一个重要的位。252
4.5 x 1015
1016
这可以通过左边的 a 和右边的 a 来实现(以一种方式),所以和:9
1
9.000000000000001
9.000000000000002
#include <stdio.h>
int main(void) {
double d1 = 9.000000000000001;
double d1 = 9.000000000000002;
if (d1 == d2) puts("Same");
return 0;
}
(1)这里的曲线是指对数刻度,其中 和 之间的距离是 。210
213
3
数字的构造方式是尾数(或分数)位创建一个由连续的 2 幂形成的基数。因此,这些位可以创建 或 (实际上在 IEEE-754 的开头有一个隐式位,但为了简单起见,我们将忽略它)。101
22 + 20
5
1
然后使用指数位进行缩放,或乘以 2 的另一种幂,因此您可以例如将其乘以得到 或得到 。5
24 = 16
80
2-10 = 1/1024
0.0048828125
而且,为了完整起见,单符号位决定它是正值还是负值。这两个,以及给你特殊数字(如和无穷大)的各种位模式,在这里也被忽略了,因为它们与问题并不真正相关。NaN
因此,为了使一个数字与另一个数字区分开来,最高有效和最不重要的位必须适合有限大小的尾数。
有关这一切如何工作的更多详细信息,请参阅我之前的回答。
评论
让我们尝试以下形式的大 16 位十进制数字整数:9 xxx xxx xxx xxx xxx
#include <stdio.h>
int main() {
//unsigned long long x0 = 9000000000000000;
unsigned long long x0 = 1uLL << 53; // 9,007,199,254,740,992
unsigned long long x1 = 9999999999999999;
unsigned long long same = 0;
for (unsigned long long x = x0; x <= x1; x++) {
double f0 = (double) x;
double f1 = (double) (x + 1);
if (f0 == f1) {
if (same++ == 0) {
printf("%llu (0x%llX) %llu (0x%llX) both value the value of %f\n", //
x, x, x + 1, x + 1, f0);
break;
}
}
}
printf("Repeats = %llu\n", same);
}
我们很快发现
9007199254740992 (0x20000000000000) 9007199254740993 (0x20000000000001) both value the value of 9007199254740992.000000
Repeats = 1
9007199254740993 (0x20000000000001)
是一个 54 位二进制数字值。普通双精度
只能精确编码最多 53 位的二进制数字值。
另一种看待它的方式是鸽子洞原理。
给定每个 2 次幂 52 个二进制数字的通用编码。在 [0.5 ...1.0),有 252 或
4,503,599,627,370,496 个不同的值。然而,在该范围内,有 5,000,000,000,000,000 个不同的 16 位十进制值 ( = [5...9], = [0-9]),因此没有足够的非重复值来唯一映射到该范围的所有 16 位十进制数字值。一些十进制值将映射到相同的 .double
double
0.add ddd ddd ddd ddd d
a
d
double
double
#include <string.h>
#include <stdio.h>
int main() {
// 1234567890123456
long long x1 = 9999999999999999; // 0x23 86F2 6FC0 FFFF
char prior[40] = "0.9999999999999999";
for (long long x = x1; --x > 0; ) {
char buf[40];
sprintf(buf, "0.%16lld", x);
double value = atof(buf);
if (value == atof(prior)) {
printf("%s %s both have value %.20g\n", prior, buf, value);
break;
}
strcpy(prior, buf);
}
printf("Done\n");
}
0.9999999999999995 0.9999999999999994 both have value 0.99999999999999944489
Done
评论
1000000000000000