什么是第一个双倍偏离相应的长三角?

Ove*_*own 5 c math double gcc

我想知道从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;
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

我猜测问题是di将我加倍,因此d == i然后差异总是为0.我还能检测到这一点 - 我更喜欢有趣的C转换比较比较字符串,这会永远.

答案:完全符合我们的预期.根据标准C/UNIX/POSIX工具,2 ^ 53 + 1 = 9007199254740993是第一个不同点.非常感谢pax的计划.我猜数学再次获胜.

pax*_*blo 12

IEE754中的双精度具有52位的精度,这意味着它们可以精确地存储数字(至少)2 51.

如果你的long是32位,它们只有(正)范围0到2 31,所以没有32位长,不能完全表示为double.对于64位长,它将(大致)为2 52,所以我将在那里开始,而不是零.

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

请记住,这可能会受到sprintf()双打的真实准确性的影响(我不这么认为,因为它没有任何麻烦,特定数字高达2 143).

这是该计划:

#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++;
    }
}
Run Code Online (Sandbox Code Playgroud)

这一直持续到:

2^51 - 1 = 2251799813685247
2^52 - 1 = 4503599627370495
2^53 - 1 = 9007199254740991
2^54 - 1 <-- bc failure
   got       [18014398509481984]
   expected  [18014398509481983]
Run Code Online (Sandbox Code Playgroud)

这是我预期失败的地方.

顺便说一句,我最初使用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]
Run Code Online (Sandbox Code Playgroud)

double的大小为8个字节(选中sizeof).事实证明,这些数字是二进制形式"1000...",可以用双精度表示更长的时间.那是当我切换到使用2 n -1来获得更好的位模式时:所有的一位.