2016-11-01 4 views
1

私は19桁の数字(1121314151617181910.0)の平方根を取ろうとしていて、 "すぐに使える" Operators.sqrtが失敗しているように見えました。私はアルゴリズム自体を書いても問題ないですが、私が気づいていなかった大きな浮動小数点のために別の平方根があるかどうか疑問に思っていました。浮動小数点のsqrtを1090000006.0よりも高くするには?

let testSqrt (square:float)= 
    let root = sqrt square 
    (root * root) = square 

testSqrt 1090000006.0 // true 
testSqrt 1090000007.0 // false 
+0

丸め誤差について知っていますか?そのため、 '(root * root)= square'があなたのために働いていないのです。 'testSqrt 2.0'も** ** falseであることに注意してください!これは、 'let root = sqrt 2.0; (ルート*ルート) - 2.0は丸め誤差のために '4.440892099e-16'です。あなたが必要とするのは、 'abs(square-root * root)'が非常に小さな数、例えば '1.0e-6'未満であることをテストすることです。 – rmunn

+0

不快な投稿です。はい、丸め誤差を認識しています。あなたは19桁の数字の平方根がないと言っていますか? – jks612

+0

'double'は53ビットの仮数を「ちょうど」持っています。これは約16小数です。 'BigInteger'を使って任意の精度で大きな有理型を実装し、Newton-Raphsonを使って' sqrt'を実装することができます。しかし、あなたが知っているように、多くの平方根は、不合理な数として定義されます。定義上、有理数で表すことはできません。また、表現ツリーとして結果を表現することも可能です。これは、あなたが得ることができる正確な表現木です。 – FuleSnabel

答えて

3

testSqrtの機能に失敗するだけでなく、大きな値でもありません。見る:

let testSqrt (square:float)= 
    let root = sqrt square 
    (root * root) = square 

testSqrt 2.0 // false! 

ここで起こっていることはrounding errorです。 sqrtが2の平方根を計算すると、不合理な数が得られますが、その数を有限数のビットに格納する必要があります。したがって、ある点で四捨五入して1.414213562を生成します。そして、あなたはそれ自体で1.414213562を掛けたとき、あなたはに非常に近いです数が、に正確に等しいではない、2入手:

let root = sqrt 2.0 
abs (2.0 - root * root) // 4.440892099e-16 

が正しくsqrtをテストするために、それはより大きくなりますいくつかの小さな値epsilonを定義する方が良いでしょうあなたの丸め誤差期待、およびroot * root値が正しい答えのepsilon内にあるであろうことを確認してください:floatタイプのみの精度の64ビットを持っているのでしかし、それは、非常に大きな数字では動作しません

let epsilon = 1.0e-6 
let testSqrt (square:float)= 
    let root = sqrt square 
    abs (square - root * root) < epsilon 
testSqrt 1090000006.0 // true 
testSqrt 1090000007.0 // now this is also true 

一緒に働くあなたはsqrtを自分で実装する必要がありますけれども、あなたが、使用できる任意の精度に行くF#でdecimalタイプがあります

let root = sqrt 1121314151617181910.0 
1121314151617181910.0 - root * root // 128.0, WAY larger than epsilon 

:、及びその割合で、あなたは丸め誤差からLOTを失っていますそれはdecimalのために実装されていないので、:

let d = decimal 4 // Prints as 4M 
let d' = 4M // Another way to write "decimal 4" 
sqrt d // Error FS0001: The type 'decimal' does not support the operator 'Sqrt' 

は注意してください:あなたは10進数浮動小数点数を変換するdecimal機能を使用している場合、数がfloatで完全にフィットする大きすぎた場合、あなたはまだエラーを丸めることがあります。

let d = decimal 1121314151617181910.0 // 1121314151617180000M; float lost some precision 
let d' = 1121314151617181910M // Correct value, 1121314151617181910M 
+2

'decimal'は任意の精度型ではなく、' float'よりも精度が高く、範囲が狭く、実行時に丸め誤差が発生しません。通貨計算:https://msdn.microsoft.com/en-us/library/system.decimal.aspx#Anchor_6 – TheInnerLight

3

浮動小数点数は正確ではありません。 0.2のような単純な値は、2進浮動小数点数を使用して正確に表現することはできません。浮動小数点数の精度が限定されているため、操作のわずかな変更によって結果が変更される可能性があります。異なるコンパイラとCPUアーキテクチャでは、異なる精度で一時的な結果が格納されるため、環境の詳細によって結果が異なります。計算を行い、その結果を期待値と比較すると、意図した結果が得られる可能性はほとんどありません。 その理由は、floatを比較する適切な方法は、次のようなものです。

abs(result - expectedResult) < 0.00001 
関連する問題