浮動小数点の丸めエラーの原因は何ですか?


62

浮動小数点演算には精度の問題があることを認識しています。私は通常、数値の固定10進数表現に切り替えるか、単にエラーを無視することでそれらを克服します。

しかし、この不正確さの原因はわかりません。浮動小数点数で丸めの問題が多いのはなぜですか?


28
正確に言うと、ほとんどの人が心配するのは実際には丸めによるエラーではなく、バイナリ浮動小数点の丸めが直感的でない方法で動作するという事実です。10進表現に切り替えると、丸めがより直感的な方法で動作しますが、その代わりに、ほぼ常に相対誤差が増加します(または補正するためにストレージスペースを増加する必要があります)。
ダニエル・プライデン

12
最もよくある混乱を解消するための私の試み:floating-point-gui.de
マイケルボルグワード

私は@DanielPrydenがされて何を意味するのかと思う「... [固定小数点]表現への切り替えがより直感的な方法で丸め振る舞いをすることができます」。どのような原因それは固定または浮動小数点数だかどうか、問題を丸めいずれかの有限のワード幅です。浮動小数点では、丸め誤差の大きさは通常、丸められる数値の大きさにほぼ比例したままです。(あなたが本当に小さくなり、「非正規化された」数になった場合を除く。)
ロバートブリストージョンソン

@robert:それはまさに私が言っていたものではありません。ほとんどの人が浮動小数点で遭遇する「エラー」は、浮動小数点そのものとは関係ありません。それが基本です。IEEE-754 floatとdoubleは、基数2で指数を使用します。つまり、小数は、10の負のべき乗(1 / 10、1 / 1000など)。これにより、0.1が0.1000001に丸められたり、同様の問題が発生するなど、直感に反する結果が生じます。
ダニエル・プライデン

基数10で浮動小数点数を実行できますdecimal。これが.NETの型の仕組みです。一方、固定小数点は異なります。範囲が制限されている限り、固定小数点は良い答えです。ただし、範囲が制限されているため、固定小数点は多くの数学アプリケーションに適さず、固定小数点数の実装はハードウェアで最適化されていないことがよくあります。
ダニエル・プライデン

回答:


82

これは、一部の小数部は、丸めることなく表現するために非常に大きな(または無限の)場所を必要とするためです。これは、バイナリなどの10進表記にも当てはまります。計算に使用する小数点以下の桁数を制限する場合(および分数表記で計算を行わないようにする場合)、単純な式でも1/3 + 1/3として丸める必要があります。結果として2/3を書く代わりに、0.33333 + 0.33333 = 0.66666と書く必要があります。これは2/3と同一ではありません。

コンピュータの場合、桁数はメモリとCPUレジスタの技術的な性質によって制限されます。内部的に使用されるバイナリ表記は、さらにいくつかの困難を追加します。通常、コンピューターは分数表記で数値を表現できませんが、一部のプログラミング言語ではこの機能が追加されるため、これらの問題をある程度回避できます。

すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと


12
スポットオン。ただし、10進数で終了する一部の数値は2進数で終了しないことにも注意してください。特に、0.1は2進数の繰り返し数であるため、浮動小数点2進数が0.1を正確に表すことはできません。
ジャックエイドリー

4
浮動小数点は、多くの小数点以下の桁だけに役立つわけではありません。32ビット整数は約40億までしかカウントできませんが、32ビットの浮動小数点数はほぼ無限に大きくなる可能性があります。
アブヒベッカート

7
特に、有限小数として表現できる分数は、分母の素因数分解が2と5のみを含むものです(たとえば、3/10と7/25は表現できますが、11/18は表現できません)。バイナリに移行すると、5の係数が失われるため、ダイアディックな有理数(1 / 4、3 / 128など)のみを正確に表現できます。
デビッドチャン

70

第一に、丸め誤差は、すべての実数無限大がコンピュータの有限メモリでは表現できない可能性があり、単一の浮動小数点変数などのメモリの小さなスライスは言うまでもないため、格納される多くの数値は単なる近似ですそれらが表すことを意図した数。

近似値ではない限られた数の値しかないため、近似値と別の数値の間の演算は近似値になるため、丸め誤差はほとんど避けられません

重要なことはすることです、彼らが問題を引き起こす可能性があるときに実現し、リスクを軽減するための措置をとります


加えて、デイビッド・ゴールドバーグ不可欠のすべてのコンピュータ科学者は、浮動小数点演算について知っておくべきこと(自分への付録として日/ Oracleが再発行され数値計算ガイドで言及された)、トルステンACCUのジャーナル過負荷が優れた走りました一連の記事リチャード・ハリスについての浮動小数点ブルース

シリーズの始まりは

数値計算には多くの落とし穴があります。リチャード・ハリスは銀の弾丸を探し始めます。

数値エラーのドラゴンは眠りから目覚めることはあまりありませんが、不注意に近づくと、不注意なプログラマーの計算に壊滅的なダメージを与えることがあります。

IEEE 754浮動小数点演算の森で彼に偶然出会ったプログラマーの中には、仲間にその公平な土地を旅することを勧めないほどのこともあります。

このシリーズの記事では、数値計算の世界を探り、浮動小数点演算と、より安全な代替として提案されているいくつかの手法とを対比します。ドラゴンの領土が実際に広範囲に及んでいること、そしてドラゴンの壊滅的な注意を恐れる場合は一般的に慎重に踏み込まなければならないことを学びます。

リチャードは、実数、合理的、非合理的、代数的、超越的な実数の分類法を説明することから始めます。その後、キャンセルエラーと実行順序の問題に進む前に、IEEE754表現の説明に進みます。

これよりも深く読めない場合、浮動小数点数に関連する問題の優れた基礎が得られます。

ただし、さらに詳しく知りたい場合は、

次に、彼はあなたのCalculus Bluesを治すのを手伝おうとすることに切り替えます

そして最後になりましたが、あります

一連の記事全体を検討する価値があり、合計66ページで、Goldberg紙の 77ページよりもまだ小さいです。

このシリーズはほぼ同じ内容をカバーしていますが、ゴールドバーグの論文よりもアクセスしやすいことがわかりました。また、Richardsの以前の記事を読んだ後、論文のより複雑な部分を理解するのが容易であることがわかりました。


このように、コメントで言及された偽のak

それらの記事の著者として、thusspakeak.com / ak / 2013/06で始まるブログwww.thusspakeak.comでインタラクティブなバージョンを作成したことに言及したいと思います。


1
それらの記事の著者として、thusspakeak.com / ak / 2013/06で始まるブログwww.thusspakeak.comでインタラクティブなバージョンを作成したことに言及したいと思います。
したがって、

@ thusspakea.kに感謝します。回答にメモを追加しましたが、これらのインタラクティブな要素は非常にうまく機能します。
マークブース

12

まあ、トルステンには決定的なリンクがあります。私は追加します:

あらゆる形式の表現には、いくつかの数値に対する丸め誤差があります。IEEE浮動小数点または10進数で1/3を表現するようにしてください。どちらも正確に行うことはできません。これはあなたの質問に答えることを超えていますが、私はこの経験則をうまく使いました:

  • ユーザーが入力した値を10進数で保存します(ほとんど確実に10進表現で入力したため、バイナリまたは16進数を使用するユーザーはほとんどいません)。そうすれば、常にユーザーが入力した正確な表現が得られます。
  • ユーザーが入力した分数を保存する必要がある場合は、分子と分母(10進数でも)を保存します。
  • 同じ数量に対して複数の測定単位(摂氏/華氏など)を持つシステムがあり、ユーザーが両方を入力できる場合、入力した値と入力した単位を保存します。変換して名前を付けて保存しないでください。精度/精度を損なうことなく実行できる場合を除き、単一の表現。すべての計算で保存された値単位を使用します。
  • マシンが生成した値をIEEE浮動小数点に保存します(これは、A / Dコンバーターを備えたアナログセンサーなどの電子測定デバイスによって生成された数値、または計算の丸められていない結果です)。シリアル接続を介してセンサーを読み取る場合、これは適用されず、すでに10進形式(18.2 Cなど)で値を提供していることに注意してください。
  • ユーザーが表示可能な合計などを10進数で保存します(銀行口座の残高など)。適切に丸めますが、その値を将来のすべての計算の最終的な値として使用します。

追加します:ARPRECやdecNumberなどの任意精度の数学パッケージの使用を検討します。
Blrfl

私は10進数ではなく(2進数ではなく)、分数の分子や分母などの整数値に対して大きな利点があります。どちらも正確な整数値を格納でき、バイナリはより効率的です。入力と出力の変換をやり直すにはいくらかコストがかかりますが、I / Oを物理的に実行するコストに圧倒される可能性があります。
キーストンプソン

10

これまで言及されていなかったと思われるのは、不安定なアルゴリズム悪条件の問題の概念です。前者を最初に取り上げますが、これは初心者の数値学者にとってより頻繁な落とし穴のようです。

(逆数)黄金比のべき乗の計算を検討してくださいφ=0.61803…。それについて移動するための1つの可能な方法は、漸化式を使用することでφ^n=φ^(n-2)-φ^(n-1)始まる、φ^0=1φ^1=φ。お気に入りのコンピューティング環境でこの再帰を実行し、結果を正確に評価された能力と比較すると、重要な数字のゆっくりとした侵食が見つかります。Mathematicaでのインスタンスの例を次に示します:

ph = N[1/GoldenRatio];  
Nest[Append[#1, #1[[-2]] - #1[[-1]]] & , {1, ph}, 50] - ph^Range[0, 51]  
{0., 0., 1.1102230246251565*^-16, -5.551115123125783*^-17, 2.220446049250313*^-16, 
-2.3592239273284576*^-16, 4.85722573273506*^-16, -7.147060721024445*^-16, 
1.2073675392798577*^-15, -1.916869440954372*^-15, 3.1259717037102064*^-15, 
-5.0411064211886014*^-15, 8.16837916750579*^-15, -1.3209051907825398*^-14, 
2.1377864756200182*^-14, -3.458669982359108*^-14, 5.596472721011714*^-14, 
-9.055131861349097*^-14, 1.465160458236081*^-13, -2.370673237795176*^-13, 
3.835834102607072*^-13, -6.206507137114341*^-13, 1.004234127360273*^-12, 
-1.6248848342954435*^-12, 2.6291189633497825*^-12, -4.254003796798193*^-12, 
6.883122762265558*^-12, -1.1137126558640235*^-11, 1.8020249321541067*^-11, 
-2.9157375879969544*^-11, 4.717762520172237*^-11, -7.633500108148015*^-11, 
1.23512626283229*^-10, -1.9984762736468268*^-10, 3.233602536479646*^-10, 
-5.232078810126407*^-10, 8.465681346606119*^-10, -1.3697760156732426*^-9, 
2.216344150333856*^-9, -3.5861201660070964*^-9, 5.802464316340953*^-9, 
-9.388584482348049*^-9, 1.5191048798689004*^-8, -2.457963328103705*^-8, 
3.9770682079726053*^-8, -6.43503153607631*^-8, 1.0412099744048916*^-7, 
-1.6847131280125227*^-7, 2.725923102417414*^-7, -4.4106362304299367*^-7, 
7.136559332847351*^-7, -1.1547195563277288*^-6}

の結果φ^41は間違った符号を持ち、さらに早い段階で計算された値と実際の値はφ^39共通の数字を共有しません(3.484899258054952* ^-9 for the computed version against the true value7.071019424062048 *^-9)。したがって、アルゴリズムは不安定であり、不正確な算術演算ではこの再帰式を使用しないでください。これは、再帰式の固有の性質によるものです。この再帰には「減衰」および「成長」ソリューションがあり、代替の「成長」ソリューションがある場合、前方ソリューションによって「減衰」ソリューションを計算しようとします数値的悲嘆のため。したがって、数値アルゴリズムが安定していることを確認する必要があります。

さて、条件の悪い問題の概念に移りましょう:数値的に何かを行う安定した方法があるかもしれませんが、それはあなたのアルゴリズムでは解決できないということです。これは問題自体の問題であり、解決方法ではありません。数値の標準的な例は、いわゆる「ヒルベルト行列」を含む線形方程式の解法です。

ヒルベルト行列

行列は、条件の悪い行列の標準的な例です。大きなヒルベルト行列を持つシステムを解こうとすると、不正確な解が返される可能性があります。

以下はMathematicaのデモです。正確な算術の結果を比較します

Table[LinearSolve[HilbertMatrix[n], HilbertMatrix[n].ConstantArray[1, n]], {n, 2, 12}]
{{1, 1}, {1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 
  1}, {1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1,
   1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 
  1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}

および不正確な算術

Table[LinearSolve[N[HilbertMatrix[n]], N[HilbertMatrix[n].ConstantArray[1, n]]], {n, 2, 12}]
{{1., 1.}, {1., 1., 1.}, {1., 1., 1., 1.}, {1., 1., 1., 1., 1.},  
  {1., 1., 1., 1., 1., 1.}, {1., 1., 1., 1., 1., 1., 1.}, 
  {1., 1., 1., 1., 1., 1., 1., 1.}, {1., 1., 1., 1., 1., 1., 1., 1., 1.},  
  {1., 1., 1., 0.99997, 1.00014, 0.999618, 1.00062, 0.9994, 1.00031, 
  0.999931}, {1., 1., 0.999995, 1.00006, 0.999658, 1.00122, 0.997327, 
  1.00367, 0.996932, 1.00143, 0.999717}, {1., 1., 0.999986, 1.00022, 
  0.998241, 1.00831, 0.975462, 1.0466, 0.94311, 1.04312, 0.981529, 
  1.00342}}

Mathematicaで試してみた場合、悪条件が現れることを警告するいくつかのエラーメッセージに気付くでしょう。)

どちらの場合も、単に精度を上げるだけでは解決できません。避けられない数字の浸食を遅らせるだけです。

これはあなたが直面するかもしれないものです。解決策は難しいかもしれません。最初は、図面に戻るか、他の人があなたよりも優れた解決策を見つけたかどうかを調べるために、ジャーナル/書籍/その他何でも探します。第二に、あなたはあきらめるか、より扱いやすい何かにあなたの問題を再定式化します。


ダイアン・オレアリーからの引用をお送りします。

人生は悪条件の問題を投げかけるかもしれませんが、不安定なアルゴリズムに落ち着く正当な理由はありません。


9

10進数の10進数は2進数では表現できないため

または、言い換えれば、1/10は分母が2の累乗を持つ分数に変換できません(これは本質的に浮動小数点数です)


11
厳密には正しくありません。0.5および0.25は2を基数として表現できます。「10を基数とするすべての10進数ではない」ということです。
スコットホイットロック

3
より正確に。すべての小数は、浮動小数点表記を使用して正確に表すことができるわけではありません(つまり、を使用して、底2と底10の両方にこの問題があります)。9*3.333333310進数で試してみて、それを比較します9*3 1/3
マーティンヨーク

1
これは、浮動小数点の混乱の最も一般的な原因です。.1 + .1 != .2これは、10進数ではなく浮動小数点バイナリエンコードが使用されるためです。
ショーンマクミラン

@SeanMcMillan:そして1.0/3.0*3.0 != 1.0、浮動小数点バイナリエンコーディングが使用されているため、3進数ではありません。
キーストンプソン

8

数学では、無数の有理数があります。32ビット変数は2 32個の異なる値しか持つことができず、64ビット変数は2 64個の値しか持つことができません。したがって、正確な表現を持たない無数の有理数があります。

1/3を完全に、または1/100を表すことができるスキームを考え出すことができました。多くの実用的な目的のために、これはあまり役に立ちません。1つの大きな例外があります。金融では、小数が頻繁に表示されます。これは主に、金融は本質的に人間の活動であり、物理的な活動ではないためです。

したがって、通常はバイナリ浮動小数点を使用し、バイナリで表現できない値はすべて丸めることを選択します。しかし、金融では、10進浮動小数点を選択し、値を最も近い10進値に丸めることがあります。


2
さらに悪いことに、無限の(数え切れないほどの)メモリー量ですべての合理性を表現できますが、実数を表現するには十分ではありません。さらに悪いことに、ほとんどすべての実数は計算可能な数ではありません。有限量のメモリでできる最善の方法は、実数の有限範囲のサブセットを近似することです。
デビッドハンメン

4
@Kevin:あなたは計算可能な数値について話しています。これは実数の小さなサブセット(メジャー0のサブセット)です。
デビッドハメン

1
最も基本的な説明については+1:有限数のビットで無限数の数値を表現しようとしています。

1
@DavidHammen:計算可能な数値は、実数の(サブセット0の)小さなサブセットですが、プログラムで作業するすべての数値は、定義上、計算可能です。
キーストンプソン

3
@Giorgio:あなたは右の表現を選択した場合は、2の平方根は、ある文字列として、例えば、表現可能"√2"。(私の古いHP-48電卓は、まさにそれを行うことができたし、その値を二乗することは、正確になった2.0。)のための表現の実数のみ可算無限あります任意の、ない計算ではありません数を得ていないことができます-有限表現は、原則として、表現可能。実際には、バイナリ浮動小数点は、表現可能な数値のセットを大幅に制限します。これは、記号表現に比べて非常に高速で、ストレージが小さいという利点があります。
キーストンプソン

-2

私が考えている浮動小数点数の唯一の本当に明らかな「丸めの問題」は、移動平均フィルターに関するものです。

$$ \ begin {align} y [n]&= \ frac {1} {N} \ sum \ limits_ {i = 0} ^ {N-1} x [ni] \&= y [n-1] + \ frac {1} {N}(x [n]-x [nN])\ \ end {align} $$

ノイズを蓄積せずにこの作業を行うには、現在のサンプルに追加する$ x [n] $が、$ N $個のサンプルを差し引く$ x [nN] $と正確に同じであることを確認する必要があります。未来。そうでない場合、異なるのは遅延ラインに詰まって出てこない小さな糞です。これは、この移動平均フィルターが、実際には$ z = 1 $にわずかに安定した極とそれをキャンセルするゼロを持つIIRで構築されているためです。しかし、それはインテグレーターであり、統合されて完全に削除されないがらくたは、インテグレーターの合計に永遠に存在します。これは、固定小数点には浮動小数点数と同じ問題がありません。


ちょっと、prog.SEフォーラムで$ LaTeX $数学マークアップが機能しないのですか?そうでない場合、それは本当に足の不自由です。
ロバートブリストージョンソン

1
meta.SOおよびリンクされた質問でこれを参照してください
AakashM
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.