c - 偏离其相应 long by delta 的第一个 double 是什么?

标签 c math gcc double

我想知道从 0d 开始的第一个 double,它偏离“相同值”的 long 一些增量,比如 1e-8。不过我在这里失败了。我试图在 C 中执行此操作,尽管我通常使用托管语言,以防万一。请帮忙。


#include <stdio.h>
#include <limits.h>
#define DELTA 1e-8

int main() {
    double d = 0; // checked, the literal is fine
    long i;
    for (i = 0L; i < LONG_MAX; i++) {
         d=i; // gcc does the cast right, i checked
         if (d-i > DELTA || d-i < -DELTA) {
              printf("%f", d);
              break;
         }
    }
}

我猜问题是 d-i 将 i 转换为 double,因此 d==i 然后差值始终为 0。我还能如何正确检测到这一点——我更喜欢有趣的 C 转换而不是比较字符串,这将需要很长时间。

回答:完全符合我们的预期。 2^53+1 = 9007199254740993 是根据标准 C/UNIX/POSIX 工具的第一个不同点。非常感谢 pax 的节目。我想数学又赢了。

最佳答案

IEE754 中的 double 具有 52 位的精度,这意味着它们可以准确地存储最多(至少)251 的数字。

如果您的 long 是 32 位的,它们将只有 0 到 231 的(正)范围,因此没有 32 位 long 不能精确表示为 double 。对于 64 位长,它将(大致)为 252 所以我会从那里开始,而不是从零开始。

您可以使用以下程序来检测故障开始发生的位置。在一个早期版本中,我依赖于这样一个事实,即连续翻倍的数字中的最后一位遵循序列 {2,4,8,6}。但是,我最终选择使用一个已知的可信工具 (bc) 来检查整个数字,而不仅仅是最后一位数字。

请记住,这可能会受到 sprintf() 的操作的影响,而不是 double 的真正准确性(我个人不这么认为,因为它有2143 以内的某些数字没有问题。

这是程序:

#include <stdio.h>
#include <string.h>

int main() {
    FILE *fin;
    double d = 1.0; // 2^n-1 to avoid exact powers of 2.
    int i = 1;
    char ds[1000];
    char tst[1000];

    // Loop forever, rely on break to finish.
    while (1) {
        // Get C version of the double.
        sprintf (ds, "%.0f", d);

        // Get bc version of the double.
        sprintf (tst, "echo '2^%d - 1' | bc >tmpfile", i);
        system(tst);
        fin = fopen ("tmpfile", "r");
        fgets (tst, sizeof (tst), fin);
        fclose (fin);
        tst[strlen (tst) - 1] = '\0';

        // Check them.
        if (strcmp (ds, tst) != 0) {
            printf( "2^%d - 1 <-- bc failure\n", i);
            printf( "   got       [%s]\n", ds);
            printf( "   expected  [%s]\n", tst);
            break;
        }

        // Output for status then move to next.
        printf( "2^%d - 1 = %s\n", i, ds);
        d = (d + 1) * 2 - 1;  // Again, 2^n - 1.
        i++;
    }
}

这一直持续到:

2^51 - 1 = 2251799813685247
2^52 - 1 = 4503599627370495
2^53 - 1 = 9007199254740991
2^54 - 1 <-- bc failure
   got       [18014398509481984]
   expected  [18014398509481983]

这是我预计它会失败的地方。

顺便说一句,我最初使用 2n 形式的数字,但这让我达到了:

2^136 = 87112285931760246646623899502532662132736
2^137 = 174224571863520493293247799005065324265472
2^138 = 348449143727040986586495598010130648530944
2^139 = 696898287454081973172991196020261297061888
2^140 = 1393796574908163946345982392040522594123776
2^141 = 2787593149816327892691964784081045188247552
2^142 = 5575186299632655785383929568162090376495104
2^143 <-- bc failure
   got       [11150372599265311570767859136324180752990210]
   expected  [11150372599265311570767859136324180752990208]

double 的大小为 8 个字节(使用 sizeof 检查)。原来这些数字是二进制形式 "1000..." 可以用 double 表示更长的时间。就在那时,我转而使用 2n-1 以获得更好的位模式:所有位。

关于c - 偏离其相应 long by delta 的第一个 double 是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/732612/

相关文章:

c++ - 零初始化、未命名、临时无符号整数

c - ty如何像数组一样定义可通过下标访问的结构体 union ?就像在 GLSL 中一样

c - 使用包含多个元素的结构的 malloc

查找唯一标识这些元素集合所需的最少元素的算法

c - IEEE 浮点异常 - 为什么?

macos - Mac gcc 非虚拟 thunk 错误

c++ - 如何允许用户重新配置编译器以与 cmake 一起使用?

c - 以编程方式启用/禁用 UNIX 网络接口(interface)

c - 是否可以应用抗锯齿/其他功能来更好地绘制拉伸(stretch)的 TIcon?

algorithm - SPOJ "abs(a-b) I"错误答案问题