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

What are two numbers of 16 decimal digits precision that map to the same double-precision float?

提问人:Jagerber48 提问时间:7/24/2023 最后编辑:paxdiabloJagerber48 更新时间:7/25/2023 访问量:115

问:

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

有人可以为我提供两个十进制精度为 16 位的数字映射到同一个双精度浮点数的示例吗?

浮点 二进制 精度 浮点精度

评论

0赞 paxdiablo 7/24/2023
再次更改它以确保它讨论精度,因为从技术上讲,它是一个 16 位十进制数。1000000000000000

答:

1赞 Jagerber48 7/24/2023 #1

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

8.000 000 000 000 001

8.000 000 000 000 002

映射到同一个浮点数。

在 Python 中,我看到

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

评论

1赞 Sam Mason 7/24/2023
nextafter可能有用的知识(在 Python 的模块中)。此外,使用该模块查看完整的十进制扩展有助于查看给定浮点数所表示的实际值mathdecimal
3赞 paxdiablo 7/24/2023 #2

在双精度中,您有大约 52 个二进制精度(这转换为大约 15 个十进制数字),可以上下缩放指数“曲线”(1)。这意味着,只有当 2 的所有这些幂都在该距离内(在曲线上)时,由二的幂组成的数字才可微分。

因此,几乎可以肯定的是,并且会变成相同的数字,因为第二个数字的两个项之间的距离在曲线上相差 60。250250 + 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 位十进制位的两个数字而言,这也是可行的,就像大约 .这还不完全达到,所以你需要寻找的是大部分是零的数字,但在尾数的顶部和底部有一个重要的位。2524.5 x 10151016

这可以通过左边的 a 和右边的 a 来实现(以一种方式),所以和:919.0000000000000019.000000000000002

#include <stdio.h>

int main(void) {
    double d1 = 9.000000000000001;
    double d1 = 9.000000000000002;

    if (d1 == d2) puts("Same");
    return 0;
}

(1)这里的曲线是指对数刻度,其中 和 之间的距离是 。2102133

数字的构造方式是尾(或分数)位创建一个由连续的 2 幂形成的基数。因此,这些位可以创建 或 (实际上在 IEEE-754 的开头有一个隐式位,但为了简单起见,我们将忽略它)。10122 + 2051

然后使用指数位进行缩放,或乘以 2 的另一种幂,因此您可以例如将其乘以得到 或得到 。524 = 16802-10 = 1/10240.0048828125

而且,为了完整起见,单符号位决定它是正值还是负值。这两个,以及给你特殊数字(如和无穷大)的各种位模式,在这里也被忽略了,因为它们与问题并不真正相关。NaN

因此,为了使一个数字与另一个数字区分开来,最高有效和最不重要的位必须适合有限大小的尾数。

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

评论

0赞 Jagerber48 7/24/2023
我看到 2^50 = 1125899906842624 是一个 16 位整数,但它看起来像 2^50 + 2^-10 = 1125899906842624.0009765625,这是一个 26 位十进制。问题是寻找映射到同一浮点数的两个 16 位十进制数,而不仅仅是映射到同一浮点数的任何两个十进制数。
0赞 paxdiablo 7/24/2023
@Jagerber48:是的,我没有仔细阅读你的问题。我留下了关于IEEE-754表示如何工作的解释,因为它对理解很重要。我还添加了可以找到两个精度为 16 位且无法区分的双精度的方法,并提供了一个示例。
2赞 chux - Reinstate Monica 7/24/2023 #3

让我们尝试以下形式的大 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 位十进制数字值。一些十进制值将映射到相同的 .doubledouble0.add ddd ddd ddd ddd daddoubledouble

#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

评论

2赞 Jagerber48 7/24/2023
蛮力搜索很好。鸽子洞原理是一个很好的明显证据,证明必须有两个 16 位精度的十进制数映射到同一个浮点数。