代码之家  ›  专栏  ›  技术社区  ›  Overflown

第一个和它对应的长的差是多少?

  •  5
  • Overflown  · 技术社区  · 16 年前

    我想知道从0d向上的第一个双精度数,它偏离了“相同值”的长度,也就是说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把我强制加倍,因此D==I,然后差值总是0。另外,我如何才能正确地检测到这一点——我更喜欢有趣的C转换而不是比较字符串,这需要永远。

    回答 :完全符合我们的预期。2^53+1=9007199254740993是标准c/unix/posix工具的第一个不同点。多谢帕克斯的帮助。我想数学又赢了。

    4 回复  |  直到 16 年前
        1
  •  12
  •   paxdiablo    12 年前

    IEE754中的双精度数精度为52位,这意味着它们可以精确存储最多(至少)2个数字。 五十一 .

    如果您的long是32位的,那么它们的(正值)范围只有0到2。 三十一 所以没有32位的长度不能精确地表示为双精度数。对于64位的长度,大约是2 五十二 所以我会从那里开始,而不是从零开始。

    您可以使用以下程序检测故障开始出现的位置。在早期的版本中,我依赖于这样一个事实:连续两倍的数字中的最后一个数字跟在序列2,4,8,6之后。然而,我最终选择使用一个已知的可信工具 (bc) 用于检查整数,而不仅仅是最后一个数字。

    记住这个 可以 受…的影响 sprintf() 而不是双打的真正准确度(我个人不这么认为,因为某些数字高达2没有问题 一百四十三 )

    这是程序:

    #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]
    

    这就是我期望它失败的地方。

    顺便说一下,我最初用的是2号表格的数字 n 但这让我想到:

    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]
    

    双字节大小为8字节 sizeof )原来这些数字是二进制的 "1000..." 它可以用双打来表示更长的时间。那是我换用2的时候 n -1要获得更好的位模式:所有一位。

        2
  •  2
  •   Craig Gidney Mihai    16 年前

    第一个长到“错误”时,投射到double将不会在1e-8之前关闭,它将在1之前关闭。只要双精度数在其意义上能与长匹配,它就能准确地表示它。

    我忘记了一个双精度和偏移的确切位数,但这会告诉你它能代表的最大大小。第一个long-to-wrong应该是二进制形式10000…,所以从1开始向左移动可以更快地找到它。

    维基百科说有52位的意义,不包括隐含的起始值1。这意味着要转换为不同值的第一个long是2^53。

        3
  •  1
  •   Andrej Panjkov    16 年前

    尽管我在讨论中不愿提及Fortran 95及其继承者,但我会提到,自1990年以来,Fortran标准提供了一个间隔内在函数,它告诉您可表示实域与给定实域之间的区别。您可以对此进行二进制搜索,在间距(x)>增量时停止。对于使用与您感兴趣的浮点模型相同的编译器(可能是IEEE754标准),您应该得到相同的结果。

        4
  •  0
  •   Bill Lynch    16 年前

    另一方面,我认为double可以精确地表示所有整数(在其界限内)。

    如果不是这样的话,那么你会想把I和D都投射到比它们中任何一个更精确的东西上。也许一个长的双人床就行了。