数学では、恒等式(1 + sqrt(2))^2 = 3 + 2*sqrt(2)
が成り立ちます。しかし、浮動小数点 (IEEE 754、単精度、つまり 32 ビットを使用) 計算ではsqrt(2)
、2 進数で正確な表現がないため、そうではありません。
では、近似値を使用sqrt(2)
すると、左側と右側で異なる結果が得られるのでしょうか? もしそうなら、なぜですか?近似値を 2 乗すると精度が大幅に低下しますか?
同等の式のうち、最も正確な結果が得られるのはどれですか?
数学では、恒等式(1 + sqrt(2))^2 = 3 + 2*sqrt(2)
が成り立ちます。しかし、浮動小数点 (IEEE 754、単精度、つまり 32 ビットを使用) 計算ではsqrt(2)
、2 進数で正確な表現がないため、そうではありません。
では、近似値を使用sqrt(2)
すると、左側と右側で異なる結果が得られるのでしょうか? もしそうなら、なぜですか?近似値を 2 乗すると精度が大幅に低下しますか?
同等の式のうち、最も正確な結果が得られるのはどれですか?
この同一性は、IEEE-754 倍精度で記述されたように計算されたときにたまたま保持されます。理由は次のとおりです。
倍精度に正しく丸められた 2 の平方根は次のとおりです。
sqrt(2) = 0x1.6a09e667f3bcd * 2^0
(表現が整然としていて、IEEE754 形式への変換がはるかに簡単なため、ここでは 16 進数を使用しています)。この場合のように、オーバーフローが発生しない場合、2 による乗算は 2 進浮動小数点では正確です。
2*sqrt(2) = 0x1.6a09e667f3bcd * 2^1
3 つ追加すると、次のようになります。
3 + 2*sqrt(2) = 0x1.7504f333f9de68 * 2^2
ただし、これは表現可能な倍精度数ではない (1 ビット幅が広すぎる) ため、結果は最も近い表現可能な数に丸められます。この値が 2 つの表現可能な数値のちょうど中間にある場合があるため、末尾にゼロ ビットがある方を選択します。
3 + 2*sqrt(2) = 0x1.7504f333f9de6 * 2^2
次に、計算の反対側です。2 の倍精度平方根に 1 を加算すると、次のようになります。
1 + sqrt(2) = 0x1.3504f333f9de68 * 2^1
これは、表現可能な倍精度数とのちょうど中間のケースでもあり、ここでも最も近い「偶数」の表現可能な数に丸められます。
1 + sqrt(2) = 0x1.3504f333f9de6 * 2^1
この値を 2 乗すると、結果は次のようになります。
(1 + sqrt(2))*(1 + sqrt(2)) = 0x1.7504f333f9de599cacbc97eaa4 * 2^2
どちらも表現可能な倍精度数ではありません。これは正確な中途半端なケースではないため、最も近い表現可能な数値に丸めるだけです。つまり、次のとおりです。
(1 + sqrt(2))*(1 + sqrt(2)) = 0x1.7504f333f9de6 * 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
限られた精度の浮動小数点数を保持するために、このような複雑な等式を当てにするべきではないからです。
数値は特定の数の2進数の小数に丸めて格納されるため、数値(0.1など)に無限に多くの2進数が含まれる場合、それらは正確ではありません。したがって、これらの数値を使用した計算の結果も正確ではなく、計算の正確な結果とのわずかな違いが予想されます。
通常、私は[(1 + sqrt(2))^ 2]-[3 + 2 * sqrt(2)] <0.00001を使用して、このような条件での同等性をテストします(もちろん、この使用法を無視する場合もあります)
もっと良い方法はありますか?
コメントは大歓迎です:)
絶対的な違いだけに頼ると、問題が発生する可能性があることに注意してください。これは、1e-5または使用するものと異なることができる十分な小数点を持つ1付近の小さな数値で機能します。しかし、より大きな数について考えてみてください。それらの数字は、限られたスペース (仮数) に格納する必要があります。また、最上位桁のみが格納されます。どういう意味ですか?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(2) の近似値を使用すると、左側と右側で異なる結果が得られるのでしょうか? もしそうなら、なぜですか?
数学的には、この等式は、これらの数値間に正確な関係があるためにのみ機能します (三角形の辺の長さと関係があります)。不正確な表現の形であいまいさを加えると、等式は成り立たなくなります。平等は二項命題であるため、問題はもはや「どちらが正しいか」ではなく、むしろ「この関係は本当に正しいのか?」です。そして答えは、「いいえ、それはもう真実ではありません」です。
近似値を 2 乗すると精度が大幅に低下しますか?
2 つの浮動小数点値を操作するたびに、精度が低下する可能性があります。特定の数値 (正確なビット表現を持つもの) に対する演算の非常に小さなサブセットは、精度を悪化させないことが保証されます。
C ++でフロートの等式コンパレータを定義した人を撃つ必要があります:>。多くの合理的な言語(SMLなど)には、floatの比較演算子がありません。私は通常、次のコードを使用します。
template < typename T >
inline bool equals( T x, T y, T precision = std::numeric_limits<T>::epsilon() )
{
return abs( x - y ) <= precision;
}
注:absもここではテンプレート化された関数であり、イプシロンのデフォルトは外部に保存されます。比較のequalsは、私の目的を目的としています。
sqrt(2) には、バイナリでの正確な表現がありません。
sqrt(2) は、10 進数、16 進数、またはその他の基数 n システムでも正確な表現を持っていません。それは無理数です。
sqrt(2) の唯一の正確な表現は sqrt(2) です。または、方程式 x 2 = 2 の解として。
倍精度で(1 + sqrt(2))^2 = 3 + 2*sqrt(2)
は、保持されているようです。C コードを参照してください。
明るい面を見てください。その方程式を作り直してsqrt
sを削除すると、適切なサイズの整数を扱うことになるため、方程式は浮動小数点で正確になります;)
不正確さは、通常、表すために小数 (.5 および .2 のべき乗以外) を必要とする数値に関連しています。
あなたの質問の別の部分に答えるには:いいえ、の表現sqrt(2)
は確かに両側で同じです。エラー (および差) は、両側で同じ数に (異なる) 演算を適用し始めるまで導入されません: 1 を足すと 2 を掛けるなど。
驚いたことに、何らかの理由で非有理数の正確な表現が必要な場合(ヒント:おそらく必要ありません)、できることがあります。連分数演算です。アイデアは1972年にさかのぼり、スーパーハッカーのビル・ゴスパーによるものです。ちなみに、このアイデアのより高度な側面は、数学の現在の研究の問題です。たとえば、この論文を参照してください。
一般的に、2つの側面はあなたに異なる結果を与えるでしょう。浮動小数点数学は、可換性および関連する特性を満たしていません。コンパイラオプションやハードウェアなど、さまざまな要因が関係しています。
あなたの方程式では、どちらの側がより正確であるかをおそらく見つけることができます(私の推測では乗算された側)が、異なる値を使用することを決定した場合、一般的には成り立たないでしょう。他の値の場合、反対側の方が正確です。
あなたの場合、二乗は結果に大きな影響を与えないはずです。
では、sqrt(2)の近似値を使用すると、左側と右側で異なる結果が得られますか?もしそうなら、なぜですか?近似値を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
浮動小数点値を比較するときは、差の絶対値を特定の許容範囲と比較するのが最善であることがわかりました。あなたはいつもそれを頼りにすることができます。
一般に、浮動小数点演算は FLT_EPSILON まで、つまり最下位ビット (IEEE 32 ビット浮動小数点数の場合は 2 -23 ) まで正確です。
また参照してください: C# では Double 型の精度は 15 桁ではありませんでしたか?
これは、sqrt(x) のような連続 (無限) 関数を離散 (有限) ステート マシンで正確に表すことができないためです。代わりに、連続関数は、0 から n までのテイラー級数展開によって離散関数に変換されます。ここで、n は表現できる最大の数です (この場合は 2^32)。コンピューターでは 0 から無限大までの合計を取ることができないため、残りのエラーが残ります。この誤差は計算できるため、離散関数が連続関数にどれだけ近いかを判断できます。
関連する方程式の詳細ときれいな TeX 表現については、http: //en.wikipedia.org/wiki/Taylor_seriesを参照してください。