(1 + sqrt(2))^2 = 3 + 2*sqrt(2)は、浮動小数点算術で満たされていますか?
-
22-09-2019 - |
質問
数学ではアイデンティティ (1 + sqrt(2))^2 = 3 + 2*sqrt(2)
真実を保持します。ただし、浮動点(IEEE 754、単一の精度IE 32ビットを使用)計算では、そうではありません。 sqrt(2)
バイナリの正確な表現はありません。
の近似値を使用します sqrt(2)
左側と右側に異なる結果を提供しますか?もしそうなら、なぜですか?近似値を四角化すると、精度が大幅に低下しますか?
同等の式のどれが最も正確な結果をもたらしますか?
解決
このアイデンティティは、IEEE-754の二重精度で書かれていると計算されたときに偶然保持されます。その理由は次のとおりです。
2つの正確に正確に丸みを帯びた2つの平方根は、次のとおりです。
sqrt(2) = 0x1.6a09e667f3bcd * 2^0
(ここでは、表現がきちんとしており、IEEE754形式への翻訳がはるかに簡単であるため、ここでヘキサデシマルを使用しています)。この場合のように、オーバーフローが発生しない場合、2の乗算はバイナリフローティングポイントで正確です。
2*sqrt(2) = 0x1.6a09e667f3bcd * 2^1
3つ追加すると、次のようになります。
3 + 2*sqrt(2) = 0x1.7504f333f9de68 * 2^2
ただし、これは表現可能な二重精度数ではありません(少し幅が広すぎます)ため、結果は最も近い表現可能な数値に丸められます。この値は2つの表現可能な数値の間の半分にあるので、ゼロビットの後続の数字を選択します。
3 + 2*sqrt(2) = 0x1.7504f333f9de6 * 2^2
これで、計算の反対側になりました。 1つを2つの二重の平方根に追加すると、次のようになります。
1 + sqrt(2) = 0x1.3504f333f9de68 * 2^1
これはまた、表現可能な二重精度数の間の正確な中間ケースであり、再び、最も近い「偶数」の表現可能な数値に丸められます:
1 + sqrt(2) = 0x1.3504f333f9de6 * 2^1
この値が二乗された場合、結果は次のとおりです。
(1 + sqrt(2))*(1 + sqrt(2)) = 0x1.7504f333f9de599cacbc97eaa4 * 2^2
これは、表現可能な二重精度番号でもありません。これはです いいえ 正確な中間ケースなので、それは単に最寄りの表現可能な数値に丸くなるだけです。
(1 + sqrt(2))*(1 + sqrt(2)) = 0x1.7504f333f9de6 * 2^2
概要: この値を2つの異なる方法で計算すると、2つの異なるラウンドシーケンスが発生しますが、最終結果は同じです。ただし、計算を2倍の精度でのみ見ました。これは、異なる算術タイプを使用して計算が実行される場合には当てはまらない場合があります。
しかし、一般的に、表現 3 + 2*sqrt(2)
バイナリIEEE-754タイプには2つの丸み(平方根と追加)のみが発生するため、より正確な場合(異なる場合)、より正確であることが予想される必要がありますが、 (1 + sqrt(2))*(1 + sqrt(2))
3つのラウンド(平方根、追加、乗算)が発生します。また、2つの違いはせいぜい1つまたは2つのビットであり、おそらくあなたの目的のために怠慢であることに注意する必要があります。
他のヒント
偶然から 0.1 + 0.2 != 0.3
限られた精度の浮動小数点数を保持するために、このような複雑な平等を頼りにしてはなりません。
数値は特定の数のバイナリ小数に丸められて保存されているため、数値(0.1など)が無限に多くのバイナリ桁を持つ場合、正確ではありません。したがって、これらの数値を使用した計算の結果は正確ではなく、計算の正確な結果に対する小さな違いが予想されます。
SQRT(2)の近似値を使用すると、左側と右側に異なる結果が得られますか?もしそうなら、なぜですか?
数学的には、この平等はこれらの数値間の正確な関係のためにのみ機能します(三角形の辺の長さに関係しています)。不正確な表現の形でファジネスを追加すると、平等はもはや真実ではありません。平等はバイナリ命題であるため、質問はもはや「どちらの側が正しい」のではなく、「この関係はまったく真実ですか?」です。答えは、「いいえ、もう真実ではない」ということです。
近似値を四角化すると、精度が大幅に低下しますか?
2つのフローティングポイント値のすべての操作は、その精度を低下させる可能性があります。特定の数値の非常に小さな操作サブセット(正確なビット表現を持つもの)は、精度を悪化させないように保証できます。
一般的に[(1 + sqrt(2))^2] - [3 + 2*sqrt(2)] <0.00001を使用して、そのような条件で平等をテストします(もちろん、場合によってはこの使用法を無視します)
より良い方法はありますか?
コメントは高く評価されています:)
男に注意してください。絶対的な違いにのみ依存すると、問題が発生する可能性があります。それは1件前後の少数で動作します。これは、1E-5または使用するものによって異なることができるほど十分な小数点を備えています。しかし、より大きな数について考えてください。それらの数字は限られたスペース(マンティッサ)に保管する必要があります。そして、最も重要な数字のみが保存されます。どういう意味ですか? 1E-5のような違いを測定できる桁を保存するためのスペースが残っていないこと!
まとめて、絶対的な比較と相対比較を同時に使用する方が良いです。
bool equal(float a, float b)
{
if (abs(a - b) < eps)
return true;
if (abs(a - b) / max(abs(a), abs(b)) < eps)
return true;
return false;
}
明るい面を見てください:あなたがその方程式を再加工して削除するなら sqrt
S、あなたは合理的にサイズの整数を扱うので、方程式は浮動小数点で正確になります;)
不正確さは、通常、表現するために小数(.5および.2のパワーを除く)を必要とする数値に関連付けられています。
あなたの質問の別の部分に答えるために:いいえ、の表現 sqrt(2)
確かに両側で同じです。エラー(および違い)は、両側に同じ数に(異なる)操作を開始するまで導入されません。1対2の乗算を追加します。
C ++のフロートの平等コンパレータを定義した人は、撃たなければなりません:>。多くの合理的な言語(SMLなど)には、フロートの比較演算子がありません。通常、次のコードを使用します。
template < typename T >
inline bool equals( T x, T y, T precision = std::numeric_limits<T>::epsilon() )
{
return abs( x - y ) <= precision;
}
注:ABSはここでもテンプレートされた機能であり、Epsilonのデフォルトは外に保存されます。比較の平等は、私の目的を目的としています。
二重の精度で、 (1 + sqrt(2))^2 = 3 + 2*sqrt(2)
保持しているようです。見る Cコード.
もう1つのアイデアを捨てるつもりです -
はい、本当です、実数の正確な平等は、コンピュータープログラミングの意味のない概念です。
しかし、実数の正確な平等が私たちの物理的現実の意味のない概念であることも事実です。
私たちの物理的現実の整数は、カウントの結果です。私たちの物理的現実の実数は測定の結果です。すべての測定にはエラーが含まれます。 2つの物理的測定値がまったく同じ値を持っていると言うことは、ナンセンスです。せいぜい、測定が可能である精度に適したある程度の精度に丸められた2つの物理的測定は等しい。
定規で鉛筆の長さを測定すると、長さが最も近い16インチになります。一対のキャリパーで測定すると、長さが100分の1000分の1000分の1になります。現実世界の測定には、常にこの種の丸めが含まれます。コンピュータープログラムで実際の測定値をシミュレートする場合、同じことをする必要があります。
実数の平等は、数学者のみにとって意味のある概念です。 (そして、そこでさえ、それは整数の平等とは異なる、そしてより複雑な概念です)。
SQRT(2)には、バイナリに正確な表現がありません。
SQRT(2)は、小数、16進体、または他のBase-Nシステムで正確な表現を持っていません。それは不合理な数です。
SQRT(2)の唯一の正確な表現はSQRT(2)です。または、方程式xの解として2 = 2.
フローティングポイント値を比較する場合、差の絶対値を特定の許容範囲と比較することが最適です。あなたはいつでもそれを頼りにすることができます。
SQRT(2)の近似値を使用すると、左側と右側に異なる結果が得られますか?もしそうなら、なぜですか?近似値を四角化すると、精度が大幅に低下しますか?
加算と乗算には、両方のエラー近似があります。乗算は、特にネストされている場合は経験的です。
以下は正確な表現ではありませんが、私のポイントを理解するのに役立ちます。
example of addition:
(float1 * float2 + float3)
float1 * float2 + float3 + mult_approximation + add_approximation
example multiplication
(float1 * (float2 + float3))
(float1 * (float2 + float3 + add_apporiximation)
float1 * (float2 + float3) + add_approximation * float1 + mult_approximation
SQRT(x)のような連続(無限)関数を表すことは、離散(有限)状態マシンで正確に実行できないためです。代わりに、連続関数は、0からnまでのTaylorシリーズの拡張を介して離散関数に変換されます。ここで、nはあなたが表すことができる最高の数です(この場合は2^32)。コンピューターで0から無限に合計を取得できないため、残りのエラーが残ります。このエラーを計算できるため、離散関数が連続関数にどれだけ近いかを判断できます。
関係する方程式の詳細ときれいなTEX表現については、次のようにしてください。http://en.wikipedia.org/wiki/taylor_series
一般的に、2つの側面が異なる結果をもたらします。浮動小数点数学は、通勤および関連する特性を満たしていません。コンパイラオプションやハードウェアなど、多くの要因が関係しています。
方程式については、おそらくどの側がより正確であるか(私の推測が掛けられた側)を見つけることができますが、異なる値を使用することを決定した場合、一般的には保持されません。反対側は、他の値に対してより正確です。
正方形は、あなたの場合に結果に大きな影響を与えるべきではありません。
驚くべきことに、何らかの理由で、非合理的な数値を正確に表現する必要がある場合(ヒント:おそらくそうではありません)、できることがあります。アイデアは1972年になり、スーパーハッカーのビル・ゴスパーによるものです-Google It Up。ちなみに、このアイデアのより高度な側面は、数学の現在の研究の問題です。例を参照してください この紙.
一般に、フローティングポイント操作は、flt_epsilon、つまり、最も重要なビット内で正確にあり、32ビットのフロートは2です。−23.
参照してください: C#のダブルタイプの精度は15桁ではありませんでしたか?