web-dev-qa-db-ja.com

10進数を正確に2進数で表現できないのはなぜですか?

SOには、浮動小数点表現についていくつかの質問が投稿されています。たとえば、10進数0.1には正確なバイナリ表現がないため、==演算子を使用して別の浮動小数点数と比較するのは危険です。浮動小数点表現の背後にある原理を理解しています。

私が理解していないのは、なぜ数学的な観点から、小数点の右側の数字が左側の数字よりも「特別」なのかということです。

たとえば、数値の整数部分は常に正確であるため、数値61.0は正確なバイナリ表現を持ちます。しかし、数値6.10は正確ではありません。私がしたことは、小数点を1桁移動するだけで、突然ExactopiaからInexactvilleに移動しました。数学的には、2つの数値に本質的な違いはありません。単なる数値です。

対照的に、10進数を別の方向に1桁移動して数値610を生成しても、Exactopiaのままです。私はその方向(6100、610000000、610000000000000)に進み続けることができますが、それらはまだ正確、正確、正確です。しかし、小数がしきい値を超えるとすぐに、数値は正確ではなくなります。

どうしたの?

編集:明確にするために、IEEEなどの業界標準の表現に関する議論を避け、数学的に「純粋な」方法であると信じているものに固執したいと思います。ベース10での位置の値は次のとおりです。

... 1000  100   10    1   1/10  1/100 ...

バイナリでは、次のようになります。

... 8    4    2    1    1/2  1/4  1/8 ...

また、これらの数値にarbitrary意的な制限はありません。位置は左右に無限に増加します。

275
Barry Brown

10進数can十分なスペースがある場合は正確に表現します-浮動小数点binaryポイント番号だけではありません。浮動10進数ポイントタイプ(.NETのSystem.Decimalなど)を使用する場合、バイナリ浮動小数点で正確に表現できない多くの値を正確に表現できます。

別の方法で見てみましょう-あなたが慣れている可能性が高いベース10では、1/3を正確に表現することはできません。 0.3333333 ...(繰り返し)です。 0.1を2進浮動小数点数として表現できない理由は、まったく同じ理由です。 3、9、および27を正確に表すことができますが、1/3、1/9、または1/27ではありません。

問題は、3が10の因数ではない素数であるということです。multiply 3による数値では問題になりません。問題が発生することなく、常に整数を乗算できます。 。しかし、divide素数で、ベースの要素ではない数である場合、問題が発生する可能性があります(およびwill 1を除こうとすると、そうなります)その番号で)。

通常、0.1は2進浮動小数点で正確に表現できない正確な10進数の最も単純な例として使用されますが、おそらく0.2は1/5であるためより単純な例であり、5は10進数と2進数の間に問題を引き起こす素数です。


有限表現の問題に対処するための補足事項:

一部の浮動小数点型は、System.Decimalのような固定サイズを持ち、Java.math.BigDecimalのような他のものは「任意に大きい」が、システムメモリまたは理論上の最大サイズに関係なく、ある時点で制限に達するアレイ。ただし、これはこの回答の主なものとはまったく別のポイントです。純粋にgenuine意的に多数のビットを使用する場合でも、10進数の0.1を浮動小数点表現で正確に表すことはできませんでした。他の方法と比較してください。任意の数の10進数を指定すると、can浮動小数点として正確に表現できる任意の数値を正確に表現できます。

347
Jon Skeet

不正確さの理由は、数値ベースの性質です。基数10では、1/3を正確に表すことはできません。 0.333になります...ただし、基数3では、1/3は0.1で正確に表され、1/2は無限に繰り返される小数(小数?)です。有限で表現できる値は、基底の一意の素因数の数に依存するため、基底30 [2 * 3 * 5]は、基底2または基底10よりも多くの小数を表すことができます。基底210 [2 * 3 * 5 * 7]。

これは、「浮動小数点エラー」とは別の問題です。不正確なのは、数十億の値がはるかに広い範囲に分散しているためです。したがって、仮数に23ビットがある場合、表現できる値は約830万個のみです。次に、8ビットの指数は、これらの値を配布するための256のオプションを提供します。このスキームにより、最も正確な小数が0の近くで発生するため、almostは0.1を表すことができます。

33
James M.

たとえば、数値の整数部分は常に正確であるため、数値61.0は正確なバイナリ表現を持ちます。しかし、数値6.10は正確ではありません。私がしたことは、小数点を1桁移動するだけで、突然ExactopiaからInexactvilleに移動しました。 数学的には、2つの数値間に本質的な違いはないはずです-それらは単なる数値です

基数10と2の詳細から少し離れてみましょう-基数bで、どの数字に終端表現があり、どの数字にないのか尋ねましょう。少し考えてみると、x b^nが整数であるような整数xが存在する場合にのみ、数値bが終了n-表現を持つことがわかります。

そのため、たとえば、x = 11/500の末尾には10表現があります。これは、n = 3を選択してから、x b^n = 22を選択できるためです。ただし、x = 1/3はサポートしません。なぜなら、nを選択しても、3を取り除くことができないからです。

この2番目の例は、要因について考えるように促し、任意のrationalx = p/q(最低条件と仮定)について、素因数分解を比較することで質問に答えることができることがわかりますbおよびqの。 qbの素因数分解にない素因数がある場合、これらの要素を取り除くために適切なnを見つけることはできません。

したがって、基数10の場合、anyp/qqには2または5以外の素因数が含まれ、終端表現はありません。

したがって、ベース10と2に戻ると、qがその素因数分解にp/qsと2sしか含まれていない場合、10表現で終わる有理数は5という形式になります。 qがその素因数分解に2sのみを含む場合、その同じ数は終了2表現を正確に持ちます。

しかし、これらのケースの1つは他のケースのサブセットです!いつでも

qには、その素因数分解に2sしかありません

それは明らかにalsotrue

qには、その素因数分解に2sと5sしかありません

または、別の言い方をすれば、p/qの終了2表現があるときはいつでも、p/qの終了10表現があるときです。ただし、逆はnotを保持します-qがその素因数分解に5を持っているときはいつでも、終了10-representationを持ちますが、 not終了2表現。これは、他の回答で言及されている0.1の例です。

そこで、あなたの質問に対する答えがあります-2の素因数は10の素因数のサブセットであるため、すべての2終端数は10終端数ですが、その逆はありません。約61対6.1ではなく、約10対2です。

最後に、いくつかの奇妙な人々が(たとえば)ベース17を使用したが、コンピューターがベース5を使用した場合、あなたの直感はこれに惑わされなかったでしょう-no (非ゼロ、非整数)両方のケースで終了した数字!

22
AakashM

根本的な(数学的な)理由は、整数を扱うとき、それらは数え切れないほど無限であるということです。

つまり、それらの数が無限にある場合でも、スキップせずにシーケンス内のすべてのアイテムを「カウント」することができます。つまり、リスト内の610000000000000th番目の位置にあるアイテムを取得したい場合は、数式を使用してそれを把握できます。

ただし、実数は数え切れないほど無限です。 「610000000000000の位置にある実際の番号を教えて」と言って返事を返すことはできません。その理由は、01の間であっても、浮動小数点値を検討している場合、無限の数の値があるためです。 2つの浮動小数点数についても同じことが言えます。

詳細:

http://en.wikipedia.org/wiki/Countable_set

http://en.wikipedia.org/wiki/Uncountable_set

更新:謝罪、質問を誤って解釈したようです。私の応答は、すべてのreal値を表すことができない理由についてであり、浮動小数点が自動的に有理数として分類されることに気付いていませんでした。

15
TM.

スキート氏へのコメントで私が言ったことを繰り返すために:私たちcanは1/3、1/9、1/27、または10進表記。追加のシンボルを追加することでそれを行います。たとえば、数値の10進数の拡張で繰り返される数字上の行。 10進数を2進数のシーケンスとして表すために必要なのは、1)2進数のシーケンス、2 )基数点、および3)シーケンスの繰り返し部分を示す他の記号。

ヘーナーの引用表記はこれを行う方法です。彼は引用記号を使用して、シーケンスの繰り返し部分を表します。記事: http://www.cs.toronto.edu/~hehner/ratno.pdf およびWikipediaエントリ: http://en.wikipedia.org/wiki/Quote_notation

表現システムにシンボルを追加できないということは何もないので、バイナリ引用表記法を使用して正確に10進の有理数を表現でき、逆も同様です。

9
ntownsend

BCD- 2進化10進数 -表現は正確です。スペース効率はあまり良くありませんが、この場合は精度を犠牲にする必要があります。

6
Alan

浮動小数点で十分な大きさの数値を作成すると(指数を作成できるため)、小数点の前も不正確になります。したがって、前提が間違っているため、あなたの質問が完全に有効だとは思いません。浮動小数点数は指数を使用して数値の大きさを表す必要があり、そのように精度も失われるため、10でシフトすると常に精度が高くなるわけではありません。

4
Daniel Lew

10進数で正確に1/3を表すことができないのと同じ理由で、0.33333(3)と言う必要があります。バイナリでは、これは同じタイプの問題ですが、異なる数のセットに対してのみ発生します。

4
James

(注:ここに2進数を示すために「b」を追加します。他のすべての数値は10進数で示します)

物事について考える1つの方法は、科学的記法のようなものです。 6.022141 * 10 ^ 23のような科学表記法で表された数値を見るのに慣れています。浮動小数点数は、仮数と指数ですが、10の代わりに2の累乗を使用する同様の形式を使用して内部的に保存されます。

61.0は、仮数と指数を使用して1.90625 * 2 ^ 5または1.11101b * 2 ^ 101bに書き換えられます。それに10を掛けて(小数点を移動)、次のことができます。

(1.90625 * 2 ^ 5)*(1.25 * 2 ^ 3)=(2.3828125 * 2 ^ 8)=(1.19140625 * 2 ^ 9)

または、仮数と指数をバイナリで入力します。

(1.11101b * 2 ^ 101b)*(1.01b * 2 ^ 11b)=(10.0110001b * 2 ^ 1000b)=(1.00110001b * 2 ^ 1001b)

数値を乗算するためにそこで行ったことに注意してください。仮数を乗算し、指数を追加しました。次に、仮数が2より大きく終了したため、指数をバンプすることで結果を正規化しました。これは、10進数の科学表記法で数値を演算した後、指数を調整するときと同じです。いずれの場合も、使用した値はバイナリで有限表現されていたため、基本的な乗算および加算操作によって出力された値も有限表現の値を生成しました。

ここで、61を10で除算する方法を検討します。まず、仮数1.90625と1.25を除算します。 10進数では、これは1.525、つまりニースの短い数字になります。しかし、これをバイナリに変換するとどうなりますか?通常の方法で行います。整数の10進数を2進数に変換するように、可能な限り最大の2のべき乗を減算しますが、負の2のべき乗を使用します。

 1.525-1 * 2 ^ 0-> 1 
 0.525-1 * 2 ^ -1-> 1 
 0.025-0 * 2 ^ -2-> 0 
 0.025-0 * 2 ^ -3-> 0 
 0.025-0 * 2 ^ -4-> 0 
 0.025-0 * 2 ^ -5-> 0 
 0.025-1 * 2 ^ -6-> 1 
 0.009375-1 * 2 ^ -7-> 1 
 0.0015625-0 * 2 ^ -8- > 0 
 0.0015625-0 * 2 ^ -9-> 0 
 0.0015625-1 * 2 ^ -10-> 1 
 0.0005859375-1 * 2 ^ -11- -> 1 
 0.00009765625 ... 

ええとああ。今、私たちは困っている。 1.90625/1.25 = 1.525は、2進法で表現した場合、繰り返し分数であることがわかります。特定のポイントを超えてゼロと仮定します。 61を10で割ったときに表示されるエラーは、次の違いです。

1.100001100110011001100110011001100110011 ... b * 2 ^ 10b
そして、言います:
1.100001100110011001100110b * 2 ^ 10b

この仮数の丸めにより、浮動小数点値に関連付けられる精度が失われます。仮数を正確に表現できる場合でも(たとえば、2つの数値を加算する場合など)、仮数が指数を正規化した後に収まらないほど多くの桁を必要とする場合、数値の損失が発生する可能性があります。

実際、この種のことは、小数を扱いやすいサイズに丸めて、最初の数桁を与えるだけで常に行われます。結果を10進数で表現するため、自然に感じられます。しかし、小数を丸めてから別の基数に変換した場合、浮動小数点の丸めによって得られる小数と同じようにlookく見えます。

4
Boojum

これはいい質問です。

あなたの質問はすべて、「数字をどのように表現するのですか?」に基づいています。

すべての数値は、10進数表現またはバイナリ(2の補数)表現で表現できます。 それらすべて!!

BUT some(それらのほとんど)は無限の数の要素を必要とします( "0"または "1"が2進位置、または "0"、 "1"から "9"までが10進表現です) )。

10進表記で1/3のように(1/3 = 0.3333333 ... <-無限数の "3"で)

バイナリで0.1のように(0.1 = 0.00011001100110011 .... <-無限の「0011」で)

すべてがその概念の中にあります。お使いのコンピューターはfinite数字のセット(10進数または2進数)しか考慮できないため、コンピューターで正確に表現できるのは一部の数字だけです...

また、Jonが言ったように、3は10の因数ではない素数なので、1/3は10を底とする有限要素の数で表すことはできません。

任意の精度の算術であっても、基数2の番号付けシステムは、61を表すことはできますが、6.1を完全に記述することはできません。

6.1では、別の表現(10進表現、浮動小数点値の表現に基数2または基数10を許可するIEEE 854など)を使用する必要があります

4
ThibThib

誰もまだこれを述べていないことに驚いています: continues fractions を使用してください。このようにして、任意の有理数をバイナリで有限に表現できます。

いくつかの例:

1/3(0.3333 ...)

0; 3

5/9(0.5555 ...)

0; 1, 1, 4

10/43(0.232558139534883720930 ...)

0; 4, 3, 3

9093/18478(0.49209871198181621387596060179673 ...)

0; 2, 31, 7, 8, 5

ここから、整数のシーケンスをメモリに保存するさまざまな既知の方法があります。

完全な精度で数値を保存することに加えて、連続分数には、最適な合理的近似など、その他の利点もあります。連続する分数の数字のシーケンスを早期に終了することを決定した場合、残りの桁(分数に再結合した場合)が可能な限り最良の分数を提供します。これは、piの近似値を見つける方法です。

Piの継続分数:

3; 7, 15, 1, 292 ...

シーケンスを1で終了すると、分数が得られます。

355/113

これは優れた合理的な近似です。

3
Nick

方程式では

2^x = y ;  
x = log(y) / log(2)

したがって、私は、バイナリのような対数ベースシステムができるかどうか疑問に思っていました、

 2^1, 2^0, 2^(log(1/2) / log(2)), 2^(log(1/4) / log(2)), 2^(log(1/8) / log(2)),2^(log(1/16) / log(2)) ........

これで問題を解決できるかもしれないので、バイナリで32.41のようなものを書きたいなら、

2^5 + 2^(log(0.4) / log(2)) + 2^(log(0.01) / log(2))

または

2^5 + 2^(log(0.41) / log(2))
2
rachit_verma

問題は、実際に数値が正確に61.0であるかどうかを実際に知らないことです。このことを考慮:


float a = 60;
float b = 0.1;
float c = a + b * 10;

Cの値は何ですか? .1には正確なバイナリ表現がないため、bは実際には.1ではないため、正確には61ではありません。

1
Dima

数字の意味が整数から非整数に変わったため、しきい値があります。 61を表すには、6 * 10 ^ 1 + 1 * 10 ^ 0があります。 10 ^ 1と10 ^ 0は両方とも整数です。 6.1は6 * 10 ^ 0 + 1 * 10 ^ -1ですが、10 ^ -1は1/10であり、これは明らかに整数ではありません。それがあなたがInexactvilleで終わる方法です。

1
Mark Ransom

分数と整数でパラレルを作成できます。 1/7などの一部の小数は、たくさんの小数がないと小数形式で表現できません。浮動小数点はバイナリベースであるため、特殊なケースは変わりますが、同じ種類の精度の問題が発生します。

1
mP.

先ほど説明したように、浮動小数点演算では、10進数の0.1を2進数で完全に表現することはできません。

浮動小数点および整数表現は、表現される数値のグリッドまたは格子を提供します。算術が行われると、結果はグリッドから落ち、丸めてグリッドに戻す必要があります。例は、バイナリグリッドの1/10です。

ある紳士が提案したように、2進化10進表現を使用する場合、グリッド上に数値を保持できますか?

0
Joe

数値61.0には正確な浮動小数点演算がありますが、all integersには当てはまりません。倍精度浮動小数点数と64ビット整数の両方に1を追加するループを記述した場合、最終的には64ビット整数が数値を完全に表すが、浮動小数点はそうではないポイントに到達します。十分な有効ビットがないためです。

小数点の右側にある近似点に到達する方がはるかに簡単です。すべての数値を2進浮動小数点で書き始めたら、もっと意味があります。

別の考え方として、基数10で61.0が完全に表現可能であり、小数点をシフトしてもそれが変わらないことに注意すると、10の累乗(10 ^ 1、10 ^ -1 )。浮動小数点では、2の累乗で乗算しても数値の精度には影響しません。完全に正確な数値が正確な表現を失う可能性があることを示すために、61.0を3で繰り返し除算してみてください。

0
John Calsbeek

無限数の有理数と、それらを表す有限数のビットがあります。 http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems を参照してください。

0
zpasternack

整数を知っていますか?各ビットは2 ^ nを表します


2 ^ 4 = 16
2 ^ 3 = 8
2 ^ 2 = 4
2 ^ 1 = 2
2 ^ 0 = 1

浮動小数点でも(いくつかの違いはありますが)ビットは2 ^ -n 2 ^ -1 = 1/2 = 0.5を表します
2 ^ -2 = 1 /(2 * 2)= 0.25
2 ^ -3 = 0.125
2 ^ -4 = 0.0625

浮動小数点バイナリ表現:

sign Exponent Fraction(非表示1が分数に追加されると思います)
B11 B10 B9 B8 B7 B6 B5 B4 B3 B2 B1 B0

0
yan bellavance

上記の高得点の答えがそれを打ち付けました。

最初に質問でベース2とベース10を混ぜてから、ベースに割り切れない数字を右側に置くと問題が発生します。 3が10の累乗にならないか、2の累乗にならないバイナリで1/5にならないため、10進数の1/3が好きです。

もう1つのコメントは、浮動小数点数であるピリオドでは決して使用しないでください。たとえそれが正確な表現であっても、いくつかの浮動小数点システムには複数の方法で正確に表現できる数値があります(IEEEはこれについて悪いです、そもそも恐ろしい浮動小数点仕様なので、頭痛の種を期待します)。ここで、小数点の右側に3がいくつあっても、1/3は電卓0.3333333の数値と等しくありません。それは十分に近いか、または近いかもしれませんが、等しくありません。そのため、2 * 1/3のようなものは、丸めに応じて2/3に等しくないと予想されます。浮動小数点で等価を使用しないでください。

0
old_timer