倍精度と単精度の違いについて読みました。ただし、ほとんどの場合、float
とdouble
は互換性があるように見えます。つまり、どちらか一方を使用しても結果に影響はないようです。これは本当ですか?フロートとダブルスはいつ交換できますか?それらの違いは何ですか?
大きな違い。
名前が示すように、 double
は float
の2倍の精度を持ちます。[1]。一般にdouble
は15桁の10進数の精度を持ち、float
は7桁の精度を持ちます。
桁数の計算方法は次のとおりです。
double
には52個の仮数ビットと1個の隠されたビットがあります:log(253)÷log(10)= 15.95桁
float
は23の仮数ビット+ 1の隠されたビットを持っています:log(224)÷log(10)= 7.22桁
この精度の損失は、反復計算が行われるときに累積されるより大きな打ち切り誤差をもたらす可能性がある。
float a = 1.f / 81;
float b = 0;
for (int i = 0; i < 729; ++ i)
b += a;
printf("%.7g\n", b); // prints 9.000023
しながら
double a = 1.0 / 81;
double b = 0;
for (int i = 0; i < 729; ++ i)
b += a;
printf("%.15g\n", b); // prints 8.99999999999996
また、floatの最大値は3e38
ですが、doubleは1.7e308
であるため、float
を使用すると、単純な場合はdouble
よりもはるかに簡単に "infinity"(特殊な浮動小数点数)をヒットできます。 60の階乗を計算します。
テスト中に、少数のテストケースにこれらの膨大な数が含まれている可能性があります。そのため、フロートを使用するとプログラムが失敗する可能性があります。
もちろん、時にはdouble
でさえ十分に正確ではないので、long double
を持っていることもあります。[1] (上記の例では、Macでは9.000000000000000066が得られます)、すべての浮動小数点型は 四捨五入エラー の影響を受けます。したがって、精度が非常に重要な場合(金銭処理など)はint
または分数クラスを使用してください。
さらに、エラーがすぐに累積するので、たくさんの浮動小数点数を合計するのに+=
を使用しないでください。 Pythonを使用している場合はfsum
を使用してください。そうでなければ、 Kahan加算アルゴリズム を実装してみてください。
[1]:CおよびC++標準は、float
、double
、およびlong double
の表現を指定していません。 3つすべてがIEEE倍精度として実装されている可能性があります。それでも、ほとんどのアーキテクチャ(gcc、MSVC、x86、x 64、ARM)では、float
は 実際にIEEE単精度浮動小数点数(binary32)であり、double
は IEEE倍精度精度の浮動小数点数(binary64).
これは、標準C99(ISO-IEC 9899 6.2.5§10)またはC++ 2003(ISO-IEC 14882-2003 3.1.9§8)標準の内容です。
float
、double
、およびlong double
の3つの浮動小数点型があります。double
型は少なくともfloat
と同程度の精度を提供し、long double
型は少なくともdouble
と同程度の精度を提供します。float
型の値の集合は、double
型の値の集合のサブセットです。double
型の値の集合は、long double
型の値の集合のサブセットです。
C++標準には、次のものが追加されています。
浮動小数点型の値表現は実装定義です。
IEEE浮動小数点規格の詳細については、優れた すべてのコンピュータ科学者が浮動小数点演算について知っておくべきこと をご覧ください。表現の詳細について学び、大きさと精度の間にトレードオフがあることに気付くでしょう。浮動小数点数表現の精度は、大きさが減少するにつれて増加します。したがって、-1と1の間の浮動小数点数は最も精度の高いものです。
二次方程式が与えられた場合: x 2 - 4.0000000 x + 3.9999999 = 0、有効数字10桁の正確な根は、 r です。1= 2.000316228および r 2= 1.999683772。
float
とdouble
を使って、テストプログラムを書くことができます。
#include <stdio.h>
#include <math.h>
void dbl_solve(double a, double b, double c)
{
double d = b*b - 4.0*a*c;
double sd = sqrt(d);
double r1 = (-b + sd) / (2.0*a);
double r2 = (-b - sd) / (2.0*a);
printf("%.5f\t%.5f\n", r1, r2);
}
void flt_solve(float a, float b, float c)
{
float d = b*b - 4.0f*a*c;
float sd = sqrtf(d);
float r1 = (-b + sd) / (2.0f*a);
float r2 = (-b - sd) / (2.0f*a);
printf("%.5f\t%.5f\n", r1, r2);
}
int main(void)
{
float fa = 1.0f;
float fb = -4.0000000f;
float fc = 3.9999999f;
double da = 1.0;
double db = -4.0000000;
double dc = 3.9999999;
flt_solve(fa, fb, fc);
dbl_solve(da, db, dc);
return 0;
}
プログラムを実行すると私になります:
2.00000 2.00000
2.00032 1.99968
数字が大きくないことに注意してください、それでもあなたはfloat
を使用してキャンセル効果を得ます。
(実際、上記は単精度または倍精度の浮動小数点数を使用して2次方程式を解く最善の方法ではありませんが、たとえ より安定した方法 を使用しても答えは変わりません。)
浮動小数点計算に含まれる数値のサイズは、最も重要なものではありません。実行されているのは関連性のある計算です。
本質的に、計算を実行していて、結果が非合理的な数または繰り返し小数である場合、その数を使用している有限サイズのデータ構造に四捨五入すると丸め誤差が生じます。 doubleはfloatの2倍のサイズなので、丸め誤差はずっと小さくなります。
テストでは、この種のエラーを引き起こす可能性がある数値を特に使用している可能性があるため、コードで適切な型を使用していることをテストしました。
32ビット長のfloat型は、7桁の精度です。非常に大きいまたは非常に小さい範囲(+/- 3.4 * 10 ^ 38または* 10 ^ -38)で値を格納できますが、有効数字は7桁です。
Double型、64ビット長、より広い範囲(* 10 ^ +/ - 308)および15桁の精度を持っています。
Long double型は公称80ビットですが、特定のコンパイラとOSのペアは、アライメントのために12〜16バイトとして格納することがあります。 long doubleは、とんでもなく巨大な19桁の精度を持つ指数を持ちます。 Microsoftは、無限の知恵で、long doubleを8バイトに制限しています。これは、普通のdoubleと同じです。
一般的に言って、あなたが浮動小数点の値/変数を必要とするときただタイプdoubleを使いなさい。式で使用されるリテラル浮動小数点値は、デフォルトでは倍精度として扱われ、浮動小数点値を返すほとんどの数学関数は倍精度を返します。 doubleを使用するだけで、頭痛や型キャストを節約できます。
Floatはdoubleより精度が劣ります。よく知っていますが、 浮動小数点演算についてWEが知っておくべきこと をよく読んでください。
私はエラーに遭遇しただけで私は永遠に把握できず、潜在的にあなたにfloat精度の良い例を与えることができます。
#include <iostream>
#include <iomanip>
int main(){
for(float t=0;t<1;t+=0.01){
std::cout << std::fixed << std::setprecision(6) << t << std::endl;
}
}
出力は
0.000000
0.010000
0.020000
0.030000
0.040000
0.050000
0.060000
0.070000
0.080000
0.090000
0.100000
0.110000
0.120000
0.130000
0.140000
0.150000
0.160000
0.170000
0.180000
0.190000
0.200000
0.210000
0.220000
0.230000
0.240000
0.250000
0.260000
0.270000
0.280000
0.290000
0.300000
0.310000
0.320000
0.330000
0.340000
0.350000
0.360000
0.370000
0.380000
0.390000
0.400000
0.410000
0.420000
0.430000
0.440000
0.450000
0.460000
0.470000
0.480000
0.490000
0.500000
0.510000
0.520000
0.530000
0.540000
0.550000
0.560000
0.570000
0.580000
0.590000
0.600000
0.610000
0.620000
0.630000
0.640000
0.650000
0.660000
0.670000
0.680000
0.690000
0.700000
0.710000
0.720000
0.730000
0.740000
0.750000
0.760000
0.770000
0.780000
0.790000
0.800000
0.810000
0.820000
0.830000
0.839999
0.849999
0.859999
0.869999
0.879999
0.889999
0.899999
0.909999
0.919999
0.929999
0.939999
0.949999
0.959999
0.969999
0.979999
0.989999
0.999999
0.83以降でわかるように、精度は大幅に低下します。
しかし、t
をdoubleに設定しても、そのような問題は起こりません。
私のプログラムを台無しにしたこの小さなエラーを理解するのに5時間かかりました。
浮動小数点数を使うとき、あなたのローカルテストがサーバサイドで行われるテストと全く同じであることを信じることはできません。環境とコンパイラはおそらくあなたのローカルシステムと最終テストが実行される場所によって異なります。特に2つの浮動小数点数を比較しようとした場合に、私はこれまでに何度かTopCoderの競合でこの問題を見ました。
2つの数値を浮動小数点数と比較する場合と同様に、組み込みの比較演算は異なります。データ型(つまり、floatまたはdouble)が異なると、結果が異なる場合があります。
Floatとdoubleの違いは、doubleはfloat変数よりも精度が高いという点です。変数をfloatとして宣言すると、小数点の後に小数点以下6桁までしか入力できません。つまり、float変数の場合、float f= 2.3333333;
//の後に小数点以下7桁が付きます。保存できる最大数
小数点の後に6桁を超える値を格納したとしても、整数は格納されず、小数点の後の最初の6桁までしか格納されません。また、この変数の小数点の後に7桁を超えて格納しようとするとエラーが発生します。この場合、次のように初期化する必要があります - float f= 2.3333334443f;
//これはコンパイラによって2.3333334として解釈されます
Doubleの場合、小数点以下15桁まで格納されます。例:double d=1.222222345675423;
//小数点以下15桁