Delta의 해당 Long에서 벗어나는 첫 번째 두 배는 무엇입니까?
문제
나는 일부 델타 (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를 그들 중 어느 것보다 더 정밀한 무언가로 캐스팅하고 싶을 것입니다. 아마도 긴 더블이 작동 할 것입니다.