당신은 합산 동안 더 높은 정밀도를 사용하는 것이지만, 나는 그 이유를 알지 못한다고 대답했습니다. 그 대답은 정확합니다. 완벽하게 만들어 낸 숫자이 단순화 된 버전을 고려해
#include <iostream>
#include <iomanip>
float w = 0.;
float calcFloat(const int* origin, int n)
{
float d = 0;
for(int k = 0; k < n; k++)
d += origin[k] * w;
return (float)d;
}
float calcDouble(const int* origin, int n)
{
double d = 0;
for(int k = 0; k < n; k++)
d += origin[k] * w;
return (float)d;
}
int main()
{
int o[] = { 1111, 22222, 33333, 444444, 5555 };
std::cout << std::setprecision(9) << calcFloat(o, 5) << '\n';
std::cout << std::setprecision(9) << calcDouble(o, 5) << '\n';
}
결과는 다음과 같습니다
6254.77979
6254.7793
그래서 입력이 두 경우 모두 동일하다하더라도, 당신은에 대한 double
을 사용하여 다른 결과를 얻을 수 중간 합계. calcDouble
을 (double)w
으로 변경하더라도 출력은이 아닙니다.
이것은 (origin[f[k].p0] + origin[f[k].p3] - origin[f[k].p1] - origin[f[k].p2])*f[k].w
의 계산이 충분히 높은 정밀도이지만 합계 도중 오류가 누적되면 피하려고하는 것임을 나타냅니다.
이것은 부동 소수점 숫자로 작업 할 때 오류가 전파되는 방식 때문입니다. The Floating-Point Guide: Error Propagation 인용 : 일반적으로
:
- 곱셈과 나눗셈은 "안전"작업입니다
- 덧셈과 뺄셈이 위험 다른 크기의 번호가 포함 된 경우 때문에, 작은 - 크기의 자리 번호가 손실됩니다.
그래서 합계에 더 높은 정밀도 유형이 필요합니다. 여기에는 추가가 포함됩니다. 대신 double
으로 정수를 곱하면 그다지 중요하지 않습니다. 시작하는 값이 float
인 것과 거의 비슷한 값을 얻을 수 있습니다 (결과가 그리 크지 않거나 매우 작지 않은 한 작은).그러나 개별 숫자 자체가 float
으로 표시 될 수있는 경우에도 매우 다른 순서를 가질 수있는 float
값을 합하면 오류가 누적되어 실제 답변에서 더 멀리 벗어납니다.
작업에서 그를 참조하십시오 :
float f1 = 1e4, f2 = 1e-4;
std::cout << (f1 + f2) << '\n';
std::cout << (double(f1) + f2) << '\n';
또는 동등하지만, 원래의 코드에 가까운 :
float f1 = 1e4, f2 = 1e-4;
float f = f1;
f += f2;
double d = f1;
d += f2;
std::cout << f << '\n';
std::cout << d << '\n';
결과는 다음과 같습니다
10000
10000.0001
두 개의 수레를 추가 잃는다를 정도. 입력 값이 같더라도 double에 float을 추가하면 올바른 답을 얻을 수 있습니다. 올바른 값을 나타내는 데 9 자리 유효 숫자가 필요하며 float
의 경우 너무 많습니다.
아마도'f [k] .w'는'double'입니다. –
@ tobi303 ehm [nope] (http://stackoverflow.com/questions/10108053/ranges-of-floating-point-datatype-in-c) – justHelloWorld
@ FrançoisAndrieux 그래서 뭐? :) 당신은 두 개의 복식을 더할 수 있고, 캐스트를하지 않고 결과를 플로트에 저장할 수 있습니까? – justHelloWorld