2017-02-09 3 views
0

私はsurf.cppのパフォーマンスを改善しようとしています。ライン140からは、この機能を見つけることができます。doubleを使用してからfloatにキャストするのはなぜですか?

Intel Advisorベクトル化解析を実行
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; 
} 

、それは(特にベクトル化で)非効率的なことができることを、「現在1つのデータ型変換」を示しています。

しかし、私の質問は、この機能を見て、なぜ著者がをdoubleとして作成し、それを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何ですか? :)あなたは2つの倍を合計し、キャストなしで浮動小数点で結果を保存できますか? – justHelloWorld

答えて

7

著者は計算中に精度を高めたいので、最終結果を丸めるだけです。これは、計算中により重要な数字を保持するのと同じです。

さらに正確には、加減算すると、誤差を累積することができます。多数の浮動小数点数が関与している場合、このエラーは相当なものになります。

+0

それは奇妙です。なぜ整数に乗算する前に 'f [k] .w'を' double' **にキャストしていないのですか?そのようにして、コードは合計のより高い精度を利用することができますが、summandに関しては決定しません。それは本当に奇妙です。 – 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引用:一般に

  • 乗算および除算は「安全な」操作である
  • 加算と減算は、危険な異なる大きさの数字が含まれる場合ので、より小さな大きさの桁番号は失われます。

合計のために高精度タイプを追加する必要があります。整数をfloatの代わりに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'; 

:2つのfloatを追加

10000                                                    
10000.0001 

を失い精度。フロートをダブルに追加すると、入力が同じであっても正しい答えが得られます。正しい値を表すには9桁の有効数字が必要ですが、それはfloatには多すぎます。

+0

* '' calcDouble'を '(double) 'に変更すると出力は変わりません" * - あなたがランダムに**を選択した場合、出力は変化しません。これは証明の近くにどこにもない、私は残念だ。 – IInspectable

+1

申し訳ありませんが、あなたは証拠を求めておらず、私はそれを与えると主張しませんでした。あなたがなぜ浮動小数点数とエラー伝播を読み上げる必要があるかの問題を 'double'で理解することができない場合は、私はそれを行うための参照を追加しました。 –

+0

@Jonathan Wakely素晴らしい例と説明。 –

関連する問題