2017-02-09 3 views
0

나는 공연을 개선하기 위해 노력하고있어 surf.cpp. 라인 (140)에서이 기능을 찾을 수 있습니다왜 double을 사용하고 float으로 변환할까요?

inline float calcHaarPattern(const int* origin, const SurfHF* f, int n) 
{ 
    double d = 0; 
    for(int k = 0; k < n; k++) 
     d += (origin[f[k].p0] + origin[f[k].p3] - origin[f[k].p1] - origin[f[k].p2])*f[k].w; 
    return (float)d; 
} 

Intel Advisor 벡터화 분석 실행을, 그것을 보여줍니다 (특히 벡터화에) 비효율적 일 수 있었다 "현재 1 개 데이터 형식 변환".

하지만 내 질문은이 기능을 살펴본 이유는 저자가 ddouble으로 만든 다음 float으로 변환했기 때문입니다. 그들이 10 진수를 원하면 float이 좋을 것입니다. 내 생각에 오는 유일한 이유는 doublefloat보다 정확하기 때문에 더 작은 숫자를 나타낼 수 있지만 최종 값은 float에 저장하기에 충분히 크지 만 d 값에 대한 테스트를 실행하지 않았기 때문입니다. .

다른 가능한 이유는 무엇입니까?

+1

아마도'f [k] .w'는'double'입니다. –

+0

@ tobi303 ehm [nope] (http://stackoverflow.com/questions/10108053/ranges-of-floating-point-datatype-in-c) – justHelloWorld

+0

@ FrançoisAndrieux 그래서 뭐? :) 당신은 두 개의 복식을 더할 수 있고, 캐스트를하지 않고 결과를 플로트에 저장할 수 있습니까? – justHelloWorld

답변

7

작성자가 계산 중에 더 높은 정밀도를 원하기 때문에 최종 결과를 반올림합니다. 이것은 계산 중 더 중요한 자릿수를 유지하는 것과 같습니다.

더 정확하게는 덧셈과 뺄셈에 오류가 누적 될 수 있습니다. 이 오류는 많은 수의 부동 소수점 숫자가 포함 된 경우에 상당 할 수 있습니다.

+0

이상합니다. 왜 정수로 곱하기 전에'f [k] .w'를'double' **으로 주조하지 않는 겁니까? 그렇게하면 코드가 합계의 더 높은 정밀도를 이용할 수 있지만 summands를 고려하지 않기로 결정합니다. 정말 이상합니다. – IInspectable

+0

1에서 n까지의 누적 만 두 배로 승격 된 것 같습니다. 내부 루프, 그 4 개의 숫자는 낮은 해상도로 유지됩니다 ... –

+0

@ IInspectable 아마별로 차이가 없기 때문에? 내 대답의 예를 참조하십시오. –

4

당신은 합산 동안 더 높은 정밀도를 사용하는 것이지만, 나는 그 이유를 알지 못한다고 대답했습니다. 그 대답은 정확합니다. 완벽하게 만들어 낸 숫자이 단순화 된 버전을 고려해

#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의 경우 너무 많습니다.

+0

*''(double) '을 사용하기 위해'calcDouble'을 변경하면 출력이 변경되지 않습니다. "* - 무작위로 선택한 ** 입력을 받으면 출력을 변경하지 않습니다. 이 증거가 어디에도 없습니다, 미안 해요. – IInspectable

+1

죄송 합니다만, 당신은 증거를 요구하지 않았으며, 나는 증거를 요구하지 않았습니다. 합계에 'double'을 사용하는 이유를 이해할 수 없다면 부동 소수점 숫자와 오류 전파를 읽어야합니다. 내가 할 수있는 참조를 추가했습니다. –

+0

@Jonathan Wakely 훌륭한 예와 설명. –

관련 문제