浮動小数点の不正確さの例
-
21-09-2019 - |
質問
コンピューターは限りなく賢く正確であるとまだ信じている新人プログラマーや素人に、浮動小数点の不正確さをどのように説明しますか?
正確だが無味乾燥な説明よりもはるかによくアイデアを理解できると思われるお気に入りの例や逸話はありますか?
これはコンピューター サイエンスの授業でどのように教えられますか?
解決
浮動小数点数に関してつまずく主な落とし穴は基本的に 2 つあります。
規模の問題。各 FP 数値には、数値全体の「スケール」を決定する指数があり、非常に小さな値も非常に大きな値も表すことができますが、それに割り当てられる桁数は限られています。異なるスケールの 2 つの数値を加算すると、大きいスケールに収める方法がないため、小さい方が「食べられる」場合があります。
PS> $a = 1; $b = 0.0000000000000000000000001 PS> Write-Host a=$a b=$b a=1 b=1E-25 PS> $a + $b 1
この場合の例えとして、大きなプールと小さじ 1 杯の水が想像できます。どちらもサイズは大きく異なりますが、個別におおよそのサイズを簡単に把握できます。ただし、ティースプーンをプールに注ぐと、ほぼプールいっぱいの水が残ったままになります。
(これを学習している人が指数表記に問題がある場合は、次の値を使用することもできます。
1
そして100000000000000000000
とか、ぐらい。)次に、バイナリとバイナリの問題があります。10 進数表現。のような数字
0.1
限られた量の 2 進数では正確に表現できません。ただし、一部の言語ではこれがマスクされます。PS> "{0:N50}" -f 0.1 0.10000000000000000000000000000000000000000000000000
ただし、数値を繰り返し加算することで、表現誤差を「増幅」することができます。
PS> $sum = 0; for ($i = 0; $i -lt 100; $i++) { $sum += 0.1 }; $sum 9,99999999999998
しかし、これを適切に説明するための良い例えが思いつきません。なぜあなたが表現できるのかは基本的に同じ問題です 1/3 正確な値を取得するには、小数部の末尾で 3 を無限に繰り返す必要があるため、およそ 10 進数でのみ表示されます。
同様に、2 進分数は、2 分の 1、4 分の 1、8 分の 1 などを表すのに適しています。しかし、10 分の 1 などを指定すると、無限に繰り返される 2 進数のストリームが生成されます。
次に、別の問題があります。ただし、大量の数値計算を行う場合を除き、ほとんどの人はこの問題につまずくことはありません。しかし、彼らはすでに問題を知っています。多くの浮動小数点数は正確な値の近似値にすぎないため、これは、特定の近似値に対して次のことを意味します。 f 実数の r 実数は無限に存在する可能性があります r1, r2, 、 ...これはまったく同じ近似にマッピングされます。これらの数値は一定の間隔にあります。そう言ってみましょう r分 可能な最小値です r その結果、 f そして r最大 可能な最大値 r これが当てはまれば、間隔が得られます [r分, r最大] その範囲内の任意の数値が実際の数値になる可能性があります r.
ここで、その数値に対して加算、減算、乗算などの計算を実行すると、精度が失われます。すべての数値は単なる近似値であるため、実際には次のように計算を実行します。 間隔. 。結果として間隔も発生し、近似誤差はますます大きくなり、それによって間隔が広がります。その計算から 1 つの数値が返される場合があります。しかし、それは単に 1つ の間隔からの数値 可能 元のオペランドの精度と計算による精度の損失を考慮した結果。
そういうのをそう呼ぶんだよ 区間演算 そして少なくとも私にとっては、それは大学の数学コースの一部でした。
他のヒント
の正確の同じ問題ます。
からベース-10システムの抱えていること、それらを表示しますあなたはまさにそれを行うことができなくなりますベース10の小数点表現として1/3を表現してみます。
ですから、「0.3333」を書いた場合、あなたは多くのユースケースのための合理的に正確な表現を持つことになります。
のないの「1/3」と同じ。はしかし、あなたは画分にその背中を移動した場合、あなたは「10000分の3333」を取得します、 「0.5」
例えば1/2などの他の画分を、容易にベース10内の有限小数表現で表すことができます。
今2ベースとベース10は、本質的に同じ問題に苦しむ:両方が、彼らは正確に表現できないことを、いくつかの数字を持っている。
。ベース-10一方では、あなたが始まる無限の表現必要があると思いベース-2に「0.1」として1/10を表す問題がない「0.000110011を..」ます。
これはどのように素人に植のためです。コンピュータは数値を表す一つの方法は、個別の単位をカウントすることです。これらは、デジタルコンピュータです。整数は、小数部分のないもの、現代のデジタルコンピュータは2の累乗数:1、2、4、8を,,,プレイス値、バイナリ数字、何とか、何とか、何とか。画分については、デジタルコンピュータは2のべき乗の逆カウント:1/2、1/4、1/8、...問題は、多くの数字は、これらの逆の力の有限数の和で表すことができないということです。より多くの場所値(より多くのビット)を使用することで、これらの「問題」の数字の表現の精度を向上させるが、それは唯一の限られたビット数を持っているので、正確にそれを得ることはありません。いくつかの数字はビットの無限の数で表すことができない。
スヌーズ...
OK、あなたは、コンテナ内の水の量を測定したい、とあなただけの3測定杯持って:フルカップ、半カップ、および四半期カップを。最後のフルカップをカウントした後、のは、残りのカップの三分の一があるとしましょう。それは正確に利用できるカップの任意の組み合わせを満たしていないため、まだあなたはそれを測定することはできません。これは、半カップを満たしていない、と四半期カップからのオーバーフローは何かを埋めるには小さすぎます。 1/3と1/4との違い - あなたがエラーを持っているので。あなたが他の測定からの誤差とそれを組み合わせると、このエラーが配合されています。
>>> 1.0 / 10
0.10000000000000001
いくつかの画分をバイナリで正確に表現することができない方法を説明します。ちょうど(1/3のような)いくつかの画分のようなベース10に正確に表すことができない。
別の例として、Cで
printf (" %.20f \n", 3.6);
は、信じられないほど提供します。
3.60000000000000008882
ここに私の単純な理解がある。
問題: 値0.45は、正確にフロートで表すことができず、0.450000018に切り上げられます。なぜそれがありますか?
回答: 45のint値は、バイナリ値101101で表されます。 それはあなたが45×10 ^ -2取ることができる場合、値0.45を行うためには正確であろう(^ 2 = 10分の45を。) あなたがベース2の代わりに10を使用しなければならないので、しかし、それは不可能です。
は10 ^ 2 = 100に最も近いので128 = 2 ^ 7であろう。必要なビットの総数が9:6値7(111)の値が45(101101)+ 3ビット分。 = 0.3515625、値45×2 ^ -7。今、あなたは深刻な不正確さの問題を抱えています。 0.3515625を0.45にほぼ近いではありません。
どのように我々は、この不正確さを改善していますか?まあ、我々は他の何かに値45と7を変更することができます。
どのように460×2 ^ -10 = 0.44921875について。あなたは今、それがその近くに少し近いのですが、まだない10の460と4ビットのために9ビットを使用しています。あなたの最初の目的の値が0.44921875た場合は、あなたがいない近似と正確に一致を得るでしょう。
だからあなたの値の計算式は、X = A×2 ^ Bになります。 A及びBは、正または負の整数値です。 もちろん、あなたがAとBが限られている値を表すビット数を知っている数字は、あなたの精度がしかし、それ以上になることができます高いです。フロートのためには32ダブルの合計数を持っている64があり、10進数128を持っています。
数値おかしなのキュート片float
にdouble
とバック1つの変換9999999.4999999999場合に観察することができます。結果はその値は明らかに近い9999999にあり、および9999999にもかかわらず9999999.499999999正しくラウンドにもかかわらず、10000000として報告されます。