39

float浮動小数点数 (つまり、double、またはlong double) の精度の値が 1 つだけなのか、それともさまざまな精度の値を持つのかについて、さまざまな回答が得られます。

float vs. double precisionと呼ばれる 1 つのトピックは、浮動小数点の精度が絶対的なものであることを暗示しているようです。

ただし、 float と double の違いという別のトピックでは、

一般に、double の精度は15 ~ 16桁です。

別の情報筋によると、

通常、float 型の変数の精度は有効桁数7 桁です。

通常、double 型の変数の精度は有効桁数16 桁です。

私の値が正確でない場合に簡単に壊れる可能性のある機密性の高いコードを使用している場合、上記のような近似値を参照するのは好きではありません。それでは、記録をまっすぐに立てましょう。浮動小数点精度は変更可能または不変ですか? またその理由は?

4

10 に答える 10

29

精度は固定されており、倍精度の場合は正確に 2 進数で 53 桁(暗黙の先頭の 1 を除外すると 52 桁) です。これは 10 進数で約 15 桁になります。


OP は、正確に 53 の 2 進数を持つことが「約」15 の 10 進数を意味する理由を詳しく説明するように私に求めました。

これを直感的に理解するために、精度の低い浮動小数点形式を考えてみましょう。倍精度数のような 52 ビットの仮数の代わりに、4 ビットの仮数を使用します。

したがって、各数値は (-1) s × 2 yyy × 1.xxxx (sは符号ビット、yyyは指数、1.xxxxは正規化された仮数) のようになります。すぐに議論するために、符号や指数ではなく、仮数のみに焦点を当てます。

1.xxxx以下は、すべての値がどのように表示されるかを示した表xxxxです (すべての丸めは、デフォルトの浮動小数点丸めモードの動作と同様に、半分から偶数になります)。

  xxxx  |  1.xxxx  |  value   |  2dd  |  3dd  
--------+----------+----------+-------+--------
  0000  |  1.0000  |  1.0     |  1.0  |  1.00
  0001  |  1.0001  |  1.0625  |  1.1  |  1.06
  0010  |  1.0010  |  1.125   |  1.1  |  1.12
  0011  |  1.0011  |  1.1875  |  1.2  |  1.19
  0100  |  1.0100  |  1.25    |  1.2  |  1.25
  0101  |  1.0101  |  1.3125  |  1.3  |  1.31
  0110  |  1.0110  |  1.375   |  1.4  |  1.38
  0111  |  1.0111  |  1.4375  |  1.4  |  1.44
  1000  |  1.1000  |  1.5     |  1.5  |  1.50
  1001  |  1.1001  |  1.5625  |  1.6  |  1.56
  1010  |  1.1010  |  1.625   |  1.6  |  1.62
  1011  |  1.1011  |  1.6875  |  1.7  |  1.69
  1100  |  1.1100  |  1.75    |  1.8  |  1.75
  1101  |  1.1101  |  1.8125  |  1.8  |  1.81
  1110  |  1.1110  |  1.875   |  1.9  |  1.88
  1111  |  1.1111  |  1.9375  |  1.9  |  1.94

それは何桁の10進数だと思いますか? 一意ではありませんが、2 桁の範囲内の各値がカバーされているという点で、2 と言えます。または、すべての一意の値をカバーする 3 と言うことができますが、小数点以下 3 桁の範囲内のすべての値をカバーするわけではありません。

議論のために、2 桁の 10 進数があるとします。10 進数の精度は、これらの 10 進数のすべての値を表すことができる桁数になります。


では、すべての数値を半分にするとどうなるでしょうか (つまり、yyy= -1 を使用しています)。

  xxxx  |  1.xxxx  |  value    |  1dd  |  2dd  
--------+----------+-----------+-------+--------
  0000  |  1.0000  |  0.5      |  0.5  |  0.50
  0001  |  1.0001  |  0.53125  |  0.5  |  0.53
  0010  |  1.0010  |  0.5625   |  0.6  |  0.56
  0011  |  1.0011  |  0.59375  |  0.6  |  0.59
  0100  |  1.0100  |  0.625    |  0.6  |  0.62
  0101  |  1.0101  |  0.65625  |  0.7  |  0.66
  0110  |  1.0110  |  0.6875   |  0.7  |  0.69
  0111  |  1.0111  |  0.71875  |  0.7  |  0.72
  1000  |  1.1000  |  0.75     |  0.8  |  0.75
  1001  |  1.1001  |  0.78125  |  0.8  |  0.78
  1010  |  1.1010  |  0.8125   |  0.8  |  0.81
  1011  |  1.1011  |  0.84375  |  0.8  |  0.84
  1100  |  1.1100  |  0.875    |  0.9  |  0.88
  1101  |  1.1101  |  0.90625  |  0.9  |  0.91
  1110  |  1.1110  |  0.9375   |  0.9  |  0.94
  1111  |  1.1111  |  0.96875  |  1.   |  0.97

以前と同じ基準で、現在は 10 進数 1 桁を扱っています。2 進浮動小数点数と 10 進浮動小数点数は互いにきれいにマッピングされないため、指数に応じて 10 進数の桁数が増減することがわかります。

同じ引数が倍精度浮動小数点数 (仮数が 52 ビット) にも適用されますが、その場合のみ、指数に応じて 15 桁または 16 桁の 10 進数が得られます。

于 2015-05-29T19:14:04.060 に答える
25

最新のコンピューターはすべて、2 進浮動小数点演算を使用しています。つまり、通常、単精度で 24 ビット、倍精度で 53 ビット、拡張精度で 64 ビットの 2 進仮数があります。(拡張精度は x86 プロセッサで使用できますが、ARM やその他のタイプのプロセッサでは使用できません。)

24、53、および 64 ビットの仮数部は、2 kと 2 k+1の間の浮動小数点数の場合、次に大きい数はそれぞれ 2 k-23、2 k-52および 2 k-63 であることを意味します。それが解決策です。各浮動小数点演算の丸め誤差は、最大でその半分です。

では、それはどのように 10 進数に変換されるのでしょうか。 場合によります

k = 0 および 1 ≤ x < 2 を取ります。分解能は 2 -23、2 -52、および 2 -63で、それぞれ約 1.19×10 -7、2.2×10 -16、および 1.08×10 -19です。これは、小数の 7、16、および 19 より少し小さいです。次に、k = 3 と
8 ≤ x < 16 を取ります。2 つの浮動小数点数の差は 8 倍になります。8 ≤ x < 10 の場合、それぞれ小数点以下 6 桁、15 未満、および 18 桁をわずかに上回ります。しかし、10 ≤ x < 16 の場合、小数点以下が 1 つ多くなります!

x が 2 k+1より少しだけ小さく、10 nより少しだけ大きい場合、10 進数の桁数が最大になります。たとえば、1000 ≤ x < 1024 の場合です。11024x < 11000 . _ _ _ 同じ 2 進精度でも、最大 1.3 桁または log 10 (2×10)まで異なる 10 進精度を生成できます。

もちろん、記事「すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと」を読むだけでもかまいません。

于 2015-05-29T19:45:15.150 に答える
6

いいえ、可変です。出発点は非常に弱いIEEE-754標準であり、メモリに格納される浮動ポインタ数の形式を特定しただけです。単精度で 7 桁、倍精度で 15 桁の精度が期待できます。

しかし、その標準の大きな欠陥は、計算の実行方法を指定していないことです。そして問題があります。特に Intel 8087 浮動小数点プロセッサは、プログラマーに多くの眠れない夜をもたらしました。そのチップの重大な設計上の欠陥は、メモリ形式よりも多くのビットで浮動小数点値を格納することです。32 または 64 の代わりに 80 ビット。この設計上の選択の背後にある理論は、これにより中間計算がより正確になり、丸め誤差が少なくなるということです。

良いアイデアのように思えますが、実際にはうまくいきませんでした。コンパイラの作成者は、中間値をできるだけ長く FPU に保存したままにするコードを生成しようとします。コード速度にとって重要なことは、値をメモリに保存することはコストがかかることです。問題は、値を保存しなければならないことが多く、FPU 内のレジスタの数が制限されており、コードが関数の境界を越える可能性があることです。その時点で、値は切り捨てられ、多くの精度が失われます。ソース コードに小さな変更を加えるだけで、大幅に異なる値が生成される可能性があります。また、最適化されていないプログラムのビルドは、最適化されたものとは異なる結果を生成します。完全に診断不可能な方法で、結果が異なる理由を知るためにマシン コードを調べる必要があります。

Intel は、この問題を解決するためにプロセッサを再設計しました。SSE 命令セットは、メモリ形式と同じビット数で計算します。ただし、理解するのは遅いですが、コンパイラのコード ジェネレータとオプティマイザを再設計することは、かなりの投資です。ビッグ 3 の C++ コンパイラはすべて切り替えられました。ただし、たとえば、.NET Framework の x86 ジッターは FPU コードを生成しますが、常に生成されます。


次に、システム エラーが発生し、変換と計算の必然的な副作用として精度が失われます。変換 まず、人間は基数 10 の数値で作業しますが、プロセッサは基数 2 を使用します。0.1 など、私たちが使用するナイス ラウンド数はプロセッサ上でナイス ラウンド数に変換できません。0.1 は 10 のべき乗の合計として完全ですが、同じ値を生成する 2 のべき乗の有限の合計はありません。それを変換すると、10 / 3 を完全に書き留めることができないのと同じ方法で、無限の数の 1 と 0 が生成されます。したがって、プロセッサに合わせて切り捨てられる必要があり、それは +/- 0.5 ビットずれている値を生成します。小数値。

そして、計算は誤差を生じます。乗算または除算により、結果のビット数が 2 倍になり、格納された値に合わせて丸められると、+/- 0.5 ビット エラーが発生します。減算は最も危険な操作であり、多くの有効数字が失われる可能性があります。たとえば、1.234567f - 1.234566f を計算すると、結果には有効数字が 1 桁しか残りません。ジャンクな結果です。ほぼ同じ値を持つ数値間の差を合計することは、数値アルゴリズムでは非常に一般的です。

過度のシステム エラーが発生することは、最終的には数学的モデルの欠陥です。例として、ガウス消去法を使用したくない場合は、精度に対して非常に不親切です。そして、常に別のアプローチを検討してください。LU 分解は優れたアプローチです。ただし、数学者がモデルの構築に関与し、結果の期待される精度を説明することはあまり一般的ではありません。Numerical Recipes のような一般的な本も精度に十分な注意を払っていませんが、より良いモデルを提案することで間接的に悪いモデルから遠ざけることができます. 結局、プログラマーはしばしば問題に行き詰まります。まあ、それは簡単だったし、誰でもそれを行うことができました。

于 2015-06-06T19:50:42.020 に答える
5

浮動小数点変数の型は、表現できる値の範囲と小数ビット (!) の数を定義します。小数と 2 進数の間には整数の関係がないため、小数は実際には近似値です。

2 番目: 別の問題は、精度の算術演算が実行されることです。1.0/3.0またはPIについて考えてみてください。このような値は、10 進数でも 2 進数でも、限られた桁数で表すことはできません。そのため、指定されたスペースに収まるように値を丸める必要があります。使用できる小数桁数が多いほど、精度が高くなります。

PI/3.0 など、複数のそのような操作が適用されていると考えてください。これには 2 回丸める必要があります。PI 自体は正確ではなく、結果も正確ではありません。これを繰り返すと精度が 2 回失われます。

floatしたがって、 andに戻るとdoublefloat標準 (C11、附属書 F、その他についても同様) によると、使用可能なビット数が少なくなるため、丸めは の場合よりも精度が低くなりますdouble。小数部が 2 桁の 10 進数 (m.ff、float と呼びます) と 4 桁の 10 進数 (m.ffff、double と呼びます) を考えてみてください。double がすべての計算に使用されている場合、結果が 2 桁の正しい小数部のみになるまで、浮動小数点数の結果で十分であっても、すでに浮動小数点数で開始している場合よりも多くの操作を行うことができます。

ARM Cortex-M4F などの一部の (組み込み) CPU では、ハードウェア FPU は folat (単精度) のみをサポートするため、double 演算ははるかにコストがかかることに注意してください。他の MCU にはハードウェア浮動小数点計算機がまったくないため、ソフトウェアでシミュレートする必要があります (非常にコストがかかります)。ほとんどの GPU では、float は double よりもはるかに安価に実行でき、場合によっては 10 倍以上も安くなります。

于 2015-05-29T19:30:56.787 に答える
5

他の回答で説明されているように、ストレージにはバイナリで正確な桁数があります。

知っておくべきことの 1 つは、CPU は内部的に異なる精度 (80 ビットなど) で操作を実行できることです。これは、そのようなコードがトリガーできることを意味します:

void Kaboom( float a, float b, float c ) // same is true for other floating point types.
{
    float sum1 = a+b+c;
    float sum2 = a+b;
    sum2 += c; // let's assume that the compiler did not keep sum2 in a register and the value was write to memory then load again.
    if (sum1 !=sum2)
        throw "kaboom"; // this can happen.
}

より複雑な計算では可能性が高くなります。

于 2015-05-29T20:32:03.393 に答える
4

ここで風変わりな回答を追加します。この質問に C++ のタグを付けたので、浮動小数点データの精度についてはまったく保証されません。実装の大部分は、浮動小数点型を実装するときにIEEE-754を使用しますが、これは必須ではありません。C++ 言語で必要な唯一のものは次のとおりです (C++ 仕様 §3.9.1.8):

浮動小数点型には、float、double、long double の 3 つがあります。double 型は少なくとも float と同じ精度を提供し、型 long double は少なくとも double と同じ精度を提供します。float 型の値のセットは、double 型の値のセットのサブセットです。double 型の値のセットは、long double 型の値のセットのサブセットです。浮動小数点型の値表現は実装定義です。整数型と浮動小数点型は、まとめて算術型と呼ばれます。標準テンプレート std::numeric_limits (18.3) の特殊化は、実装の各算術型の最大値と最小値を指定するものとします。
于 2015-05-29T19:41:03.423 に答える
3

a を格納するために必要なスペースの量はfloat一定であり、同様に a double; ただし、有用な精度の量は相対的に一般的に変化ます。ゼロに非常に近い精度はあまり良くありません。2 番目に小さい正の値は、最小の値の 2 倍の大きさになり、ゼロより無限に大きくなります。ただし、ほとんどの範囲で、前述のように精度が異なります。floatdouble

多くの場合、その範囲全体で相対精度の変動が 2 倍未満の型を使用することは実際的ではありませんが、精度の変動により、計算結果が本来よりもはるかに不正確になる場合があることに注意してください。たとえば、 を考えてみましょう16777215.0f + 4.0f - 4.0f。すべての値はfloat同じスケールを使用して正確に表現でき、大きな値に最も近い値は +/- 16,777,215 の 1 部ですが、最初の加算ではfloat、値が 1 部で区切られている範囲の一部の結果が得られます。結果は 16,777,220 に丸められます。したがって、4 を引くと、16,777,215 ではなく 16,777,216 になります。floatnearのほとんどの値について16777216、足し算4.0fと引き算4.0f元の値は変更されませんが、ブレークオーバー ポイントで精度が変化すると、最下位のビットが余分にずれて結果が生じます。

于 2015-05-30T19:44:18.947 に答える