문제

나는 일부 델타 (1e-8)에 의해 "동일한 값"의 긴 시간에 따라 0D 위로에서 첫 번째 두 배를 알고 싶다. 그래도 나는 여기서 실패하고있다. 보통 관리하는 언어를 사용하지만 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;
         }
    }
}

문제는 DI 캐스트 I를 두 배로 늘리고 D == I로서 차이는 항상 0입니다. 영원히 가져 가라.

대답: 정확히 우리가 예상했던 것과 같습니다. 2^53+1 = 9007199254740993은 표준 C/UNIX/POSIX 도구에 따른 첫 번째 차이점입니다. 그의 프로그램에 대한 Pax에게 감사드립니다. 그리고 수학이 다시이기는 것 같아요.

도움이 되었습니까?

해결책

IEE754의 복식은 52 비트의 정밀도를 가지고있어 숫자를 정확하게 보관할 수 있습니다.51.

당신의 시간이 32 비트 인 경우, (양수) 범위는 0 ~ 2입니다.31 따라서 정확히 이중으로 표현할 수없는 길이는 32 비트가 없습니다. 64 비트의 경우 (대략) 2가됩니다.52 그래서 나는 0이 아닌 거기에서 시작할 것입니다.

다음 프로그램을 사용하여 실패가 발생하는 위치를 감지 할 수 있습니다. 이전 버전은 연속적으로 두 배가되는 숫자의 마지막 숫자가 시퀀스 {2,4,8,6}을 따른다는 사실에 의존했습니다. 그러나 나는 결국 알려진 신뢰할 수있는 도구를 사용하기로 결정했습니다. (bc) 마지막 숫자뿐만 아니라 정수를 확인합니다.

이것을 명심하십시오 5월 행동의 영향을받습니다 sprintf() 두 배의 실제 정확성보다는 (특정 숫자로 문제가 없었기 때문에 개인적으로 생각하지 않습니다.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++;
    }
}

이것은 계속 될 때까지 계속 진행됩니다.

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 모든 비트.

다른 팁

더블로 캐스트 할 때 '잘못'이라는 첫 번째는 1E-8에 의해 꺼지지 않을 것입니다. 이중은 1 씩 꺼집니다. 이중이 중요성이 길어질 수있는 한 정확하게 나타납니다.

나는 정밀 대 오프셋에 대해 더블의 비트가 얼마나 많은지 정확히 잊어 버렸지 만, 그것은 당신에게 그것을 나타내는 최대 크기를 알려줍니다. 첫 번째 오랫동안 잘못된 시간에는 이진 양식 100000이 있어야하므로 1에서 시작하여 왼쪽 시프트를 통해 훨씬 더 빨리 찾을 수 있습니다.

Wikipedia는 의미와 52 비트가 암시 적 시작 1을 계산하지 않고 52 비트를 말합니다. 이는 다른 값으로 캐스트되는 첫 번째 길이 2^53이라는 것을 의미해야합니다.

이 토론에서 Fortran 95와 후계자를 언급하기는 주저하지만, 1990 년 이후 Fortran은 표준 실수의 차이점이 무엇인지 알려주는 간격의 고유 기능을 제공했다고 언급 할 것입니다. 간격 (x)> 델타를 간격 할 때 중지하고 이에 대한 이진 검색을 수행 할 수 있습니다. 관심있는 플로팅 포인트 모델 (IEEE754 표준 일 가능성이 높음)과 동일한 부동 소수점 모델을 사용하는 컴파일의 경우 동일한 결과를 얻을 수 있습니다.

손으로, 나는 복식이 모든 정수를 정확하게 나타낼 수 있다고 생각했다.

그렇지 않다면, 당신은 I와 D를 그들 중 어느 것보다 더 정밀한 무언가로 캐스팅하고 싶을 것입니다. 아마도 긴 더블이 작동 할 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top