浮動小数点丸めエラーのソリューション


18

多くの数学的計算を扱うアプリケーションを構築する際に、特定の数値が丸め誤差を引き起こすという問題に遭遇しました。

私は浮動小数点が正確はないことを理解していますが、問題は正確な数値をどのように処理して浮動小数点の丸めが問題を引き起こさないようにするのですか?


2
あなたが直面している特定の問題はありますか?テストを行う方法はたくさんありますが、問題がある場合は大丈夫です。複数の回答が可能な質問は、Q&A形式にはあまり適していません。あなたが抱えている問題を、アイデアや提言のためにネットを投げかけるのではなく、1つの正しい答えが得られるような方法で定義できれば最高です。

私は、多くの数学的計算でソフトウェアアプリケーションを構築しています。NUNITまたはJUNITのテストが良いことを理解していますが、数学計算の問題にどのようにアプローチするかについてのアイデアが欲しいです。
JNL

1
テストする計算の例を教えてください。1つは通常、生の数学のユニットテストではありません(独自の数値型をテストする場合を除きます)が、テストするようなものdistanceTraveled(startVel, duration, acceleration)をテストします。

1つの例は小数点を扱うことです。たとえば、dist x-0からx = 14.589までの特別な設定で壁を構築し、次にx = 14.589からx = end of endのいくつかの配置を作成するとします。バイナリに変換されたときの距離.589は同じではありません....特に、14.589 + 0.25がバイナリで14.84と等しくない場合など、いくつかの距離を追加する場合...混乱しないことを望みますか?
-JNL

1
@MichaelTは質問を編集してくれてありがとう。たくさん助けました。これは初めてなので、質問の組み立て方があまり良くありません。:) ...しかし、すぐに良いでしょう。
JNL

回答:


22

浮動小数点の丸めのない代替数値タイプを作成するには、3つの基本的なアプローチがあります。これらの共通のテーマは、代わりにさまざまな方法で整数演算を使用することです。

理性

分子と分母で整数と有理数として数値を表します。番号15.589はとして表されw: 15; n: 589; d:1000ます。

0.25(つまりw: 0; n: 1; d: 4)に追加すると、LCMを計算してから2つの数値を追加します。これは多くの状況でうまく機能しますが、互いに素な多数の有理数を使用している場合、非常に大きな数になる可能性があります。

不動点

全体と小数部分があります。すべての数値はその精度に丸められます(その単語はありますが、どこにあるかは知っています)。たとえば、小数点が3つある固定小数点を使用できます。 15.589+ 0.250589 + 250 % 1000、小数部分の加算になります(そして、すべての桁上げが行われます)。これは、既存のデータベースで非常にうまく機能します。前述のように、丸めがありますが、それがどこにあるか知っていて、必要以上に正確になるように指定できます(小数点以下3桁まで測定しているので、4に固定します)。

浮動小数点

値と精度を保存します。 15.58915589値と3精度0.25として保存されますが25、とは保存され2ます。これにより、任意の精度を処理できます。これは、JavaのBigDecimalの内部(最近見ていない)が使用しているものだと思います。ある時点で、この形式から取り出して表示する必要があります。これには、丸めが含まれる場合があります(ここでも、場所を制御します)。


表現の選択を決定したら、これを使用する既存のサードパーティライブラリを見つけるか、独自のライブラリを作成できます。独自のコードを作成するときは、必ず単体テストを行い、正しく計算していることを確認してください。


2
これは良いスタートですが、もちろん、丸めの問題を完全に解決するわけではありません。π、e、√2 などの無理数には厳密な数値表現はありません。正確な表現が必要な場合は記号で表現する必要があり、丸め誤差を最小限に抑えたい場合は可能な限り遅く評価する必要があります。
カレブ

@Calebは無理数の場合、丸めが問題を引き起こす可能性がある場所を超えて評価する必要があります。たとえば、22/7はパイの0.1%の精度で、355/113は10 ^ -8の精度です。小数点以下3桁までの数値のみを使用している場合、3.141592653を使用すると、小数点以下3桁の丸め誤差を回避できます。

@MichaelT:有理数を追加するために、LCMを見つける必要はありません。また、後で「LSBゼロ」をキャンセルするのが速く、絶対に必要な場合にのみ完全に単純化します。一般に有理数の場合、通常は「分子/分母」のみ、または「分子/分母<<指数」です(「全体+分子/分母」ではありません)。また、「浮動小数点」は浮動小数点表現であり、「固定サイズの浮動小数点」と区別するために「任意のサイズの浮動小数点」と表現する方が適切です。
ブレンダン

あなたの用語のいくつかは少し不確かです-浮動小数点は意味がありません-あなたは浮動小数点を言っていると思います。
jk。

10

浮動小数点値に丸めの問題があり、丸めの問題に遭遇する必要がない場合、論理的には唯一の措置は浮動小数点値を使用しないことです。

ここで、「浮動小数点変数を使用せずに整数以外の値を含む数学を実行するにはどうすればよいですか」という質問になります。答えは任意精度データ型です。ハードウェアではなくソフトウェアで実装する必要があるため、計算は遅くなりますが、正確です。使用している言語を言わなかったので、パッケージを推奨することはできませんが、ほとんどの一般的なプログラミング言語で利用できる任意の精度のライブラリがあります。


私は現在VC ++を使用しています...しかし、他のプログラミング言語に関する情報もありがたいです。
JNL

浮動小数点値がなくても、ラウンドの問題が発生します。
チャド

2
@Chad True、しかし、目標は丸めの問題を排除することではありません(常に存在するため、使用するベースには正確な表現を持たない数値があり、無限のメモリと処理能力がないため)、それはあなたがやろうとしている計算に影響を与えないポイントまで減らしてください。
イケル

@Ikerその通りです。あなたも、質問をしている人も、彼らが達成しようとしている正確な計算と望みの精度を指定しています。彼は銃を数論にジャンプさせる前に、最初にその質問に答える必要があります。言うだけlot of mathematical calculationsでは役に立ちませんし、答えもありません。ほとんどの場合(通貨を扱っていない場合)、floatで十分です。
チャド

@Chadそれは公正な点です。彼らが必要とする精度レベルを正確に伝えるにはOPからのデータが確かに十分ではありません。
イケル

7

浮動小数点演算は、通常、非常に正確(10進数で15桁double)で非常に柔軟です。精度の桁数を大幅に削減する数学を実行しているときに問題が発生します。ここではいくつかの例を示します。

  • 減算のキャンセル:1234567890.12345 - 1234567890.12300、結果0.0045の精度は小数点以下2桁のみです。これは、同じ大きさの2つの数値を減算するたびに発生します。

  • 精度の嚥下:と1234567890.12345 + 0.123456789012345評価され1234567890.24691、第2オペランドの最後の10桁が失われます。

  • 乗算:2つの15桁の数値を乗算すると、結果には30桁の数字が格納されます。ただし、保存できないため、最後の15ビットは失われます。これは、特にsqrt()(と同様に、sqrt(x*x + y*y)結果が7.5桁の精度しか持たない場合に厄介です。

これらは、注意する必要がある主な落とし穴です。そして、それらを認識したら、それらを回避する方法で数学を定式化することができます。たとえば、ループで値を何度も増分する必要がある場合は、これを行わないでください。

for(double f = f0; f < f1; f += df) {

数回の反復の後、大きい方fはの精度の一部を飲み込みdfます。さらに悪いことに、エラーがdf合計されると、小さいほど全体的な結果が悪化するという直感に反する状況になります。より良いこれを書いてください:

for(int i = 0; i < (f1 - f0)/df; i++) {
    double f = f0 + i*df;

増分を1回の乗算で組み合わせるため、結果fは15桁の10進数まで正確になります。

これは単なる例であり、他の理由による精度の低下を回避する方法は他にもあります。しかし、関与する値の大きさを考え、ペンと紙で計算を行い、各ステップの後に固定桁数に丸めた場合に何が起こるかを想像することは、すでに非常に役立ちます。


2

問題がないことを確認する方法:浮動小数点演算の問題について学習するか、問題のある人を雇うか、常識を使用します。

最初の問題は精度です。多くの言語では、「float」と「double」(「double precision」を表すdouble)があり、多くの場合、「float」は約7桁の精度を提供しますが、doubleは15を提供します。精度が問題になる可能性のある状況では、15桁は7桁よりもはるかに優れています。わずかに問題の多い多くの状況で、「double」を使用することはそれを回避することを意味し、「float」はそうしないことを意味します。会社の時価総額が7,000億ドルだとしましょう。これをフロートで表し、最下位ビットは$ 65536です。doubleを使用して表します。最下位ビットは約0.012セントです。ですから、あなたが本当に何をしているのか本当にわかっていない限り、フロートではなくダブルを使用します。

2番目の問題は、原則の問題です。同じ結果が得られるはずの2つの異なる計算を行う場合、丸め誤差が原因ではないことがよくあります。等しいはずの2つの結果は「ほぼ等しい」です。2つの結果が互いに近い場合、実際の値は等しい可能性があります。または、そうではないかもしれません。あなたはそれを覚えておく必要があり、「xは間違いなくyより大きい」または「xは間違いなくyより小さい」または「xとyは等しいかもしれない」と言う関数を書いて使用する必要があります。

この問題は、「xを最も近い整数に切り捨てる」など、丸めを使用するとさらに悪化します。120 * 0.05を掛けると、結果は6になりますが、得られるのは「6に非常に近い数」です。次に、「最も近い整数に切り捨てる」場合、その「6に非常に近い数」は「6よりわずかに小さい」場合があり、5に丸められる場合があります。6未満であれば、結果が6にどれだけ近いかは関係ありません。

そして第三に、いくつかの問題は困難です。つまり、迅速で簡単なルールはありません。コンパイラが「long double」をより正確にサポートしている場合は、「long double」を使用して、違いが生じるかどうかを確認できます。違いがない場合は、OKであるか、本当に難しい問題があります。それがあなたが期待する種類の違いを作るなら(12番目の小数での変化のように)、あなたはおそらく大丈夫です。結果が本当に変わる場合は、問題があります。助けを求める。


1
浮動小数点演算については「常識」はありません。
whatsisname

詳細についてはこちらをご覧ください。
gnasher729

0

ほとんどの人は、実際に問題を別の場所に移動しただけで、BigDecimalの叫び声が2倍になったときに間違いを犯します。Doubleは符号ビットを提供します:1ビット、指数幅:11ビット。有効桁数:53ビット(52が明示的に保存されます)。doubleの性質により、interger全体が大きくなると、相対的な精度が失われます。ここで使用する相対精度を計算するために、以下があります。

計算におけるdoubleの相対精度は、次のfoluma 2 ^ E <= abs(X)<2 ^(E + 1)を使用します

epsilon = 2 ^(E-10)%16ビット浮動小数点の場合(半精度)

 Accuracy Power | Accuracy -/+| Maximum Power | Max Interger Value
 2^-1           | 0.5         | 2^51          | 2.2518E+15
 2^-5           | 0.03125     | 2^47          | 1.40737E+14
 2^-10          | 0.000976563 | 2^42          | 4.39805E+12
 2^-15          | 3.05176E-05 | 2^37          | 1.37439E+11
 2^-20          | 9.53674E-07 | 2^32          | 4294967296
 2^-25          | 2.98023E-08 | 2^27          | 134217728
 2^-30          | 9.31323E-10 | 2^22          | 4194304
 2^-35          | 2.91038E-11 | 2^17          | 131072
 2^-40          | 9.09495E-13 | 2^12          | 4096
 2^-45          | 2.84217E-14 | 2^7           | 128
 2^-50          | 8.88178E-16 | 2^2           | 4

つまり、+ /-0.5(または2 ^ -1)の精度が必要な場合、数値の最大サイズは2 ^ 52です。これより大きく、浮動小数点数間の距離が0.5より大きい。

+/- 0.0005(約2 ^ -11)の精度が必要な場合、数値の最大サイズは2 ^ 42です。これより大きく、浮動小数点数間の距離が0.0005より大きい。

本当にこれ以上の答えを出すことはできません。ユーザーは、必要な計算を実行するときに必要な精度とその単位値(メートル、フィート、インチ、mm、cm)を把握する必要があります。ほとんどの場合、シミュレートしたい世界の規模に応じて、単純なシミュレーションではfloatで十分です。

言うまでもないことですが、100メートルx 100メートルの世界をシミュレートすることだけを目的としている場合は、2 ^ -45に近い精度のどこかになります。これは、cpu内の最新のFPUがネイティブタイプサイズ以外の計算を行う方法には入らず、計算が完了すると(FPU丸めモードに応じて)ネイティブタイプサイズに丸められます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.