映射到同一个双精度浮点数的两个 16 位小数精度的数字是多少?

问题描述 投票:0回答:3

据说,在双精度浮点数覆盖的范围内,任何用15位精度表示的十进制数都可以用一个特定的浮点数唯一标识。但是,如果我们包含更多的精度数字(即 16),我们可能会看到两个或更多十进制数对应于同一个浮点数。请参阅 https://www.exploringbinary.com/decimal- precision-of-binary-floating-point-numbers/ 了解更多详细信息。

有人可以向我提供一个映射到相同双精度浮点数的 16 位小数精度的数字的示例吗?

floating-point binary precision floating-accuracy
3个回答
3
投票

在双精度中,您有大约 52 位二进制精度(这相当于大约 15 位十进制数字),可以按比例放大和缩小指数“曲线”(1)。这意味着只有当所有这些 2 的幂彼此之间的距离都在该距离内(在曲线上)时,由 2 的幂组成的数字才可微分。

因此,

250
250 + 2-10
几乎肯定会变成相同的数字,因为第二个数字的两项之间的距离在曲线上相差 60。

运行以下 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
,所以你需要寻找的是大部分为零但尾数顶部和底部有一个重要的一位的数字。

这可以通过左侧的

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的幂形成的基数。因此,

101
位可以创建
22 + 20
5
(IEEE-754 中实际上在开头有一个隐式的
1
位,但为了简单起见,我们将忽略它)。

然后使用 exponent 位进行缩放,或乘以另一个 2 的幂,因此您可以将

5
乘以
24 = 16
得到
80
,或
2-10 = 1/1024
得到
0.0048828125

并且,为了完整性,单个符号位决定它是正值还是负值。这两者,以及为您提供特殊数字(如

NaN
和无穷大)的各种位模式,在这里也被忽略,因为它们与问题并不真正相关。

因此,为了使一个数字与另一个数字可区分,最高有效位和最低有效位必须符合有限大小的尾数。

请参阅我之前的回答,了解有关这一切如何运作的更多详细信息。


1
投票

发布此问题后不久,我在链接文章的评论中找到了答案。 16位小数

8.000 000 000 000 001

8.000 000 000 000 002

映射到同一个浮动。

在Python中,我明白了

>>> float(8.000000000000001) == float(8.000000000000002)
True

1
投票

让我们尝试使用 9 xxx xxx xxx xxx xxx:

 形式的大 16 
decimal

位整数
#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 位二进制数字值。常见的
double
只能完全 编码最多 53 位有效的二进制数字值。


另一种看待它的方式鸽子洞原理

给定每个 2 的幂 52 个二进制数字的通用

double
编码。在 [0.5 ... 1.0) 之间,有 252 或 4,503,599,627,370,496 个不同的
double
值。然而在这个范围内,有 5,000,000,000,000,000 个不同的 16 位十进制值,其形式为 0.add ddd ddd ddd ddd d (
a
= [5...9],
d
= [0-9]),因此没有足够的不同
double
值来唯一映射到 的所有 16 位十进制数字值那个范围。一些小数值将映射到相同的
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

© www.soinside.com 2019 - 2024. All rights reserved.