あなたは、合計の間により高い精度を使用すると答えたことに疑問を呈しましたが、私は理由を見ません。その答えは正しい。完全に作らアップの数字で、この簡易版を考えてみましょう:
#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
には多すぎます。
おそらく 'f [k] .w'は' double'です。 –
@ tobi303 ehm [nope](http://stackoverflow.com/questions/10108053/ranges-of-floating-point-datatype-in-c) – justHelloWorld
@FrançoisAndrieux何ですか? :)あなたは2つの倍を合計し、キャストなしで浮動小数点で結果を保存できますか? – justHelloWorld