浮動小数点演算には精度の問題があることを知っています。私は通常、数値の固定10進数表現に切り替えるか、単にエラーを無視することで、それらを克服します。
しかし、私はこの不正確さの原因が何であるかわかりません。浮動小数点数で丸めの問題が非常に多いのはなぜですか?
これは、一部の分数は、丸めずに表現するために非常に大きな(または無限の)場所を必要とするためです。これは、2進数またはその他の10進数表記と同じです。計算に使用する小数点以下の桁数を制限する場合(および分数表記での計算を避ける場合)、単純な式でさえ1/3 + 1/3として丸める必要があります。結果として2/3と書く代わりに、0.33333 + 0.33333 = 0.66666と書く必要がありますが、これは2/3とは異なります。
コンピュータの場合、桁数はメモリとCPUレジスタの技術的な性質によって制限されます。内部で使用されるバイナリ表記は、さらにいくつかの困難を追加します。コンピュータは通常、分数表記で数値を表現できませんが、一部のプログラミング言語ではこの機能が追加されているため、これらの問題をある程度回避することができます。
thorsten には決定的な link があります。追加します:
表現の形式によっては、いくつかの数値に対して丸め誤差が生じます。 IEEE浮動小数点数または10進数で1/3を表現してみてください。どちらも正確に行うことはできません。これはあなたの質問に答えるだけではありませんが、私はこの経験則をうまく使いました:
これまでに言及されていないように見えるのは、不安定なアルゴリズムと悪条件の問題の概念です。前者について最初に説明します。これは、初心者の数値論者にとってはより頻繁な落とし穴のようです。
(逆数)黄金比の累乗の計算を考えてみましょう_φ=0.61803…
_;これを実行する1つの可能な方法は、_φ^0=1
_と_φ^1=φ
_で始まる再帰式φ^n=φ^(n-2)-φ^(n-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 value
_ 7.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で試してみた場合は、悪条件の警告メッセージがいくつか表示されます。)
どちらの場合も、単に精度を上げるだけでは解決できません。それは数字の避けられない侵食を遅らせるだけです。
これはあなたが直面するかもしれないものです。解決策は難しいかもしれません。最初に、あなたは製図板に戻るか、ジャーナル/本/何でも調べて、他の誰かがあなたよりも優れた解決策を考え出していないかを見つけます。もう1つは、あきらめるか、問題をより扱いやすいものに再定式化することです。
Dianne O'Learyからの引用を残しておきます。
人生は私たちにいくつかの悪条件の問題を投げかけるかもしれませんが、不安定なアルゴリズムに落ち着く正当な理由はありません。
10進数の10進数は2進数では表現できないため
つまり、1/10は、分母で2の累乗の小数に変換することはできません(これは基本的に浮動小数点数です)。
数学では、有理数は無限にあります。 32ビット変数は2つしか持つことができません32 異なる値、64ビット変数のみ264 値。したがって、正確な表現を持たない無数の有理数があります。
1/3を完全に、または1/100を表すことを可能にするスキームを考え出すことができます。多くの実用的な目的では、これはあまり役に立ちません。大きな例外が1つあります。金融では、小数がポップアップすることがよくあります。これは主に、金融は本質的に人間の活動であり、物理的な活動ではないためです。
そのため、通常は2進浮動小数点を使用し、2進数で表現できない値は丸めます。しかし、金融では、10進浮動小数点を選択し、値を最も近い10進値に丸めます。