Steve Jessop이 제공 한 답변에서 극단적 인 예를 시도했습니다.
#include <iostream>
#include <iomanip>
#include <cmath>
int main()
{
long billion = 1000000000;
double big = 1.0;
double small = 1e-9;
double expected = 2.0;
double sum = big;
for (long i = 0; i < billion; ++i)
sum += small;
std::cout << std::scientific << std::setprecision(1) << big << " + " << billion << " * " << small << " = " <<
std::fixed << std::setprecision(15) << sum <<
" (difference = " << std::fabs(expected - sum) << ")" << std::endl;
sum = 0;
for (long i = 0; i < billion; ++i)
sum += small;
sum += big;
std::cout << std::scientific << std::setprecision(1) << billion << " * " << small << " + " << big << " = " <<
std::fixed << std::setprecision(15) << sum <<
" (difference = " << std::fabs(expected - sum) << ")" << std::endl;
return 0;
}
다음과 같은 결과를 얻었습니다.
1.0e+00 + 1000000000 * 1.0e-09 = 2.000000082740371 (difference = 0.000000082740371)
1000000000 * 1.0e-09 + 1.0e+00 = 1.999999992539933 (difference = 0.000000007460067)
첫 번째 줄의 오류는 두 번째 줄의 10 배 이상 더 큽니다.
위 코드에서 double
s를 float
s로 변경하면 다음과 같은 결과가 나타납니다.
1.0e+00 + 1000000000 * 1.0e-09 = 1.000000000000000 (difference = 1.000000000000000)
1000000000 * 1.0e-09 + 1.0e+00 = 1.031250000000000 (difference = 0.968750000000000)
두 답변 모두 2.0에 가깝지는 않지만 두 번째 답변은 약간 더 가깝습니다.
double
Daniel Pryden이 설명한대로 Kahan 합계 ( s 포함) 사용 :
#include <iostream>
#include <iomanip>
#include <cmath>
int main()
{
long billion = 1000000000;
double big = 1.0;
double small = 1e-9;
double expected = 2.0;
double sum = big;
double c = 0.0;
for (long i = 0; i < billion; ++i) {
double y = small - c;
double t = sum + y;
c = (t - sum) - y;
sum = t;
}
std::cout << "Kahan sum = " << std::fixed << std::setprecision(15) << sum <<
" (difference = " << std::fabs(expected - sum) << ")" << std::endl;
return 0;
}
정확히 2.0 :
Kahan sum = 2.000000000000000 (difference = 0.000000000000000)
위 코드에서 double
s를 float
s로 변경해도 다음과 같은 결과가 나타납니다 .
Kahan sum = 2.000000000000000 (difference = 0.000000000000000)
Kahan이 갈 길인 것 같습니다!