浮動小数点数が不正確なのはなぜですか?


198

一部の数値が浮動小数点数として格納されると、精度が失われるのはなぜですか?

たとえば、10進数9.2は2つの10進整数(92/10)の比率として正確に表すことができ、両方とも2進数()で正確に表すことができます0b1011100/0b1010。ただし、浮動小数点数として保存された同じ比率が正確に次の値になることはありません9.2

32-bit "single precision" float: 9.19999980926513671875
64-bit "double precision" float: 9.199999999999999289457264239899814128875732421875

そのように見える単純な数が64ビットのメモリで表現するには「大きすぎる」のはなぜでしょうか。



2
参照してください壊れ数学浮いている
クリスRoofe

回答:


241

ほとんどのプログラミング言語では、浮動小数点数は指数表記と仮数(仮数とも呼ばれる)を使用して、科学表記法とよく似ています。非常に単純な数は9.2、実際にはこの部分です:

5179139571476070 * 2 -49

指数-49と仮数は5179139571476070です。一部の 10進数をこの方法で表すことができない理由は、指数と仮数の両方が整数でなければならないためです。つまり、すべての浮動小数点数は、整数の2の累乗を掛けた整数でなければなりません。

9.2は単純かもしれませんが、nが整数値に制限されている場合92/10102 nとして表すことはできません。


データを見る

まず、32ビットと64ビットを作成するコンポーネントを確認するためのいくつかの関数float。出力(Pythonの例)のみに関心がある場合は、これらをグロスします。

def float_to_bin_parts(number, bits=64):
    if bits == 32:          # single precision
        int_pack      = 'I'
        float_pack    = 'f'
        exponent_bits = 8
        mantissa_bits = 23
        exponent_bias = 127
    elif bits == 64:        # double precision. all python floats are this
        int_pack      = 'Q'
        float_pack    = 'd'
        exponent_bits = 11
        mantissa_bits = 52
        exponent_bias = 1023
    else:
        raise ValueError, 'bits argument must be 32 or 64'
    bin_iter = iter(bin(struct.unpack(int_pack, struct.pack(float_pack, number))[0])[2:].rjust(bits, '0'))
    return [''.join(islice(bin_iter, x)) for x in (1, exponent_bits, mantissa_bits)]

この関数の背後には多くの複雑さがあり、説明するのはかなり正解ですが、興味がある場合、私たちの目的にとって重要なリソースはstructモジュールです。

Python floatは64ビットの倍精度数です。C、C ++、Java、C#などの他の言語では、倍精度には別の型がありdouble、多くの場合64ビットとして実装されます。

この例を使用して関数を呼び出すと9.2、次のようになります。

>>> float_to_bin_parts(9.2)
['0', '10000000010', '0010011001100110011001100110011001100110011001100110']

データの解釈

戻り値を3つのコンポーネントに分割したことがわかります。これらのコンポーネントは次のとおりです。

  • 符号
  • 指数
  • 仮数(仮数または分数とも呼ばれる)

符号

符号は、最初のコンポーネントに1ビットとして格納されます。説明するのは簡単0です。フロートが正の数であることを意味します。1それは否定的であることを意味します。ので9.2肯定的である、私たちの符号値です0

指数

指数は11ビットとして中央のコンポーネントに格納されます。我々の場合には、0b10000000010。10進数では、それは値を表します1026。このコンポーネントの癖を使用すると、に等しい数を減算しなければならないことである2 1 - (ビット数) - 1真の指数を取得します。私たちの場合、それは0b1111111111(10進数1023)を引いて真の指数0b00000000011(10進数3)を取得することを意味します 。

仮数

仮数は52ビットとして3番目のコンポーネントに格納されます。ただし、このコンポーネントにも奇妙な点があります。この癖を理解するために、次のように、科学表記法の数を検討してください。

6.0221413x10 23

仮数部は6.0221413です。科学表記法の仮数は常に1つのゼロ以外の数字で始まることを思い出してください。同じことがバイナリにも当てはまりますが、バイナリには2桁しかありません:01。だから、バイナリ仮数部は、常にで始まります1!floatが格納されている場合、1スペースを節約するために、2進数の仮数の前部は省略されます。真の仮数を取得するには、3番目の要素の前に配置する必要があります

1.0010011001100110011001100110011001100110011001100110

3番目のコンポーネントに格納されたビットは、実際には、基数ポイントの右側にある仮数の小数部分を表すため、これには単なる加算だけではありません。

10進数を処理するときは、10の累乗で乗算または除算して「小数点を移動」します。2進数では、2の累乗で乗算または除算することで同じことができます。3番目の要素は52ビットなので、その2 52は、それを右に52の場所を移動するには:

0.0010011001100110011001100110011001100110011001100110

10進表記では、これを取得するために除算675539944105574するのと同じ4503599627370496です0.1499999999999999。(これは、正確に2進数で表現できる比率の1つの例ですが、およそ10進数でのみです。詳細については、675539944105574/4503599627370496を参照してください。)

3番目のコンポーネントを小数に変換したので、加算1すると真の仮数が得られます。

コンポーネントの要約

  • 符号(最初のコンポーネント):0正、1
  • 指数(中間部品):減算2 (ビット数)1 - 1 -真の指数を取得します
  • 仮数(最後のコンポーネント):2 (ビット数)1割り、真の仮数を取得するために加算します

数の計算

3つの部分をすべてまとめると、次の2進数が与えられます。

1.0010011001100110011001100110011001100110011001100110 x 10 11

これを2進数から10進数に変換できます。

1.1499999999999999 x 2 3(不正確!)

そして、乗算し9.2て、浮動小数点値として格納された後、()で開始した数値の最終的な表現を明らかにします。

9.1999999999999993


分数として表す

9.2

数値を作成したので、単純な分数に再構成することができます。

1.0010011001100110011001100110011001100110011001100110 x 10 11

仮数を整数にシフトします。

10010011001100110011001100110011001100110011001100110 x 10 11-110100

10進数に変換:

5179139571476070 x 2 3-52

指数を引く:

5179139571476070 x 2 -49

負の指数を除算に変換します。

5179139571476070/2 49

指数を掛ける:

5179139571476070/562949953421312

どちらが等しい:

9.1999999999999993

9.5

>>> float_to_bin_parts(9.5)
['0', '10000000010', '0011000000000000000000000000000000000000000000000000']

すでに仮数は4桁で、その後にゼロがたくさん続くことがわかります。しかし、ペースを見ていきましょう。

バイナリ科学表記を組み立てます。

1.0011 x 10 11

小数点をシフトします。

10011 x 10 11-100

指数を引く:

10011 x 10 -1

2進数から10進数へ:

X 2 19 -1

除算の負の指数:

19/2 1

指数を掛ける:

19/2

等しい:

9.5



参考文献


1
逆の方法を示す素晴らしいチュートリアルもあります-数値の10進数表現を前提として、同等の浮動小数点をどのように構築するか。「長い除算」のアプローチは、数値を表現しようとした後に「剰余」になる方法を非常に明確に示しています。あなたの答えを本当に「正規」にしたい場合は、追加する必要があります。
フローリス

1
Pythonと浮動小数点について話している場合は、リンクに少なくともPythonチュートリアルを含めることをお勧めします。docs.python.org / 3.4/ tutorial/ floatingpoint.html これは、ワンストップのガイドになるはずです。 Pythonプログラマー向けの浮動小数点問題に関するリソース。何らかの方法で不足している場合(そしてほぼ確実に不足している場合)、更新または変更について、Pythonバグトラッカーで問題を開いてください。
マークディキンソン

@mhlesterこれがコミュニティーWikiに変わった場合は、私の答えを自由に組み込んでください。
Nicu Stiurca、2014

5
この回答は、おそらく初心者向けの最良の紹介なので、間違いなくfloating-point-gui.deにもリンクしているはずです。IMO、それは「すべてのコンピュータ科学者が知っておくべきこと...」を上回るべきです-最近では、ゴールドバーグの論文を合理的に理解できる人々は、通常、すでにそれをよく知っています。
Daniel Pryden 14年

1
「これは、正確に2進数で表現できる比率の1つの例ですが、およそ10進数でのみです」。本当じゃない。これらの「2の累乗に対する数」の比率はすべて、10進数で正確です。概算では、10進数を短くするだけです-便宜上。
Rick Regan

29

これは完全な答えではありませんが(mhlesterはすでに重複しない多くの適切な根拠をカバーしています)、数値の表現が作業している基数にどれだけ依存しているかを強調したいと思います。

分数2/3を考慮

good-ol 'base 10では、通常、次のように書きます。

  • 0.666 ...
  • 0.666
  • 0.667

これらの表現を見ると、最初の表現だけが数学的に分数に等しい場合でも、それぞれを分数2/3に関連付ける傾向があります。2番目と3番目の表現/近似には、0.001のオーダーのエラーがあります。これは、9.2と9.1999999999999993の間のエラーよりも実際にははるかに悪いものです。実際、2番目の表現は正しく丸められていません!それでも、2/3の概算として0.666の問題はないので、ほとんどのプログラムで9.2がどのように概算されるかについては、実際には問題ありません(はい、一部のプログラムでは重要です。)

基数

ですから、ここで数の基数は重要です。ベース3で2/3を表現しようとした場合、

(2/3)10 = 0.2 3

つまり、ベースを切り替えることにより、同じ数に対して正確で有限の表現が得られます。要点は、任意の数値を任意の基数に変換できても、すべての有理数は、一部の基数では正確な有限表現を持ち、他の基数ではそうではないということです。

この点を理解するために、1/2を見てみましょう。この完全に単純な数値は、基数10と2で正確に表現されていても、基数3で繰り返し表現する必要があることに驚くかもしれません。

(1/2)10 = 0.5 10 = 0.1 2 = 0.1111 ... 3

浮動小数点数が不正確なのはなぜですか?

多くの場合、それらは2を基数(数字が繰り返される)で有限に表現できない有理数を概算し、一般に、任意の基数で有限の数を表すことができない可能性のある実際の(おそらく不合理な)数を概算しています。


3
つまり、言い換えると、base-3base-10がに最適であるのと1/3同じように最適です。どちらの分数もbase-2では機能しません1/10
mhlester '20

2
@mhlesterはい。そして一般的に、底Nは分母がその分数Nまたはその倍数である分数に最適です。
Nicu Stiurca、2014

2
そしてこれは、いくつかの数値ツールボックスが「何が何で割られたか」を追跡し、その過程ですべての有理数に対して「無限の精度」を維持できる1つの理由です。ちょうど物理学者がπ等の要因が相殺される場合に備えて、最後の可能な瞬間まで方程式を象徴的に保ちたいのと同じように。
フローリス

3
@Floris基本的な計算のみを実行する(つまり、入力の合理性を維持する)アルゴリズムが、入力が(可能性が高い)有理かどうかを判断し、通常の浮動小数点算術を使用して計算を実行してから、有理数を再推定するケースも見ました丸め誤差を修正するための最後の近似。特に、Matlabの縮小行階層形式アルゴリズムはこれを実行し、数値の安定性を大幅に向上させます。
Nicu Stiurca、2014

@SchighSchagh-興味深い、私はそれを知りませんでした。数値の安定性は、倍精度の今日では十分に教えられていないことを知っています。つまり、多くの美しいアルゴリズムの優雅さについて、多くの人が学習を怠っています。自分のエラーを計算して修正するアルゴリズムが本当に好きです。
フローリス

13

他のすべての答えは良いですが、まだ1つ欠けていることがあります:

(例えばπ、無理数を表現することは不可能であるsqrt(2)log(3)正確に、など)!

そして、それが実際に彼らが不合理と呼ばれている理由です。世界のビットストレージの量は、それらを1つでも保持するのに十分ではありません。シンボリック算術のみが精度を保持できます。

あなたがあなたの数学の必要性を有理数に制限するならば、精度の問題だけが扱いやすくなるけれども。あなたは(おそらく非常に大きな)整数のペアを格納する必要があるだろうab分数で表す数を保持しますa/b。あなたのすべての算術は高校の数学と同じように分数で行われなければなりません(例えばa/b * c/d = ac/bd)。

しかし、もちろん、あなたはまだトラブルの同じ種類に実行するだろうpisqrtlogsin、などの関与しています。

TL; DR

ハードウェアアクセラレーション演算では、限られた量の有理数しか表現できません。表現できないすべての数値は概算です。一部の数値(つまり、不合理)は、システムに関係なく表現できません。


4
興味深いことに、不合理な根拠は存在します。Phinary、例えば。
Veedrac 14年

5
無理数は、その基数で(のみ)表すことができます。たとえば、piはベースpiで10です
phuclv 2014年

4
ポイントは引き続き有効です:一部の数値は、システムに関係なく表現できません。ベースを変更しても何も得られないため、他のいくつかの数値を表すことができなくなります。
LumpN 2014年

4

実数は無限に多く(列挙できないほど多く)、有理数は無限にあります(列挙することは可能です)。

浮動小数点表現は有限なもの(コンピューターのあらゆるものと同様)であるため、多くの数値を表現することは不可避です。特に、64ビットでは、18,446,744,073,709,551,616の異なる値のみを区別できます(これは、無限大と比較して何もありません)。標準規約では、9.2はその1つではありません。いくつかの整数mとeについては、m.2 ^ eの形式をとることができます。


たとえば、10に基づく別の記数法を考え出すと、9.2は正確な表現になります。しかし、他の数値、たとえば1/3は、表現することが依然として不可能です。


また、倍精度浮動小数点数は非常に正確です。15桁までの非常に広い範囲の任意の数値を表すことができます。日常の計算では、4桁または5桁で十分です。生涯のミリ秒ごとにカウントしたい場合を除いて、これらの15は実際には必要ありません。


1

9.2を2進浮動小数点で表現できないのはなぜですか?

浮動小数点数は、桁数が制限されており、基数ポイントが移動可能な位置番号付けシステムです(わずかに簡略化しています)。

分数は、分母の素因数(分数がその最小項で表される場合)が底の因数である場合、位置番号付けシステムで有限桁数を使用して正確に表現できます。

10の素因数は5と2であるため、ベース10では、a /(2 b 5 c)の形式の任意の割合を表すことができます。

一方、2の唯一の素因数は2であるため、2を底とすると、a /(2 b)の形式の分数しか表現できません。

なぜコンピュータはこの表現を使用するのですか?

これは、操作するのに簡単な形式であり、ほとんどの目的で十分に正確であるためです。基本的に同じ理由で、科学者は「科学表記」を使用し、各ステップで結果を妥当な桁数に丸めます。

(たとえば)32ビットの分子と32ビットの分母を使用して、分数形式を定義することは確かに可能です。IEEE倍精度浮動小数点では表現できない数を表現できますが、同様に、そのような固定サイズの分数形式では表現できない倍精度浮動小数点で表現できる多くの数があります。

ただし、大きな問題は、そのような形式は計算を行うのが面倒なことです。2つの理由があります。

  1. 各数値の表現を1つだけにしたい場合は、各計算の後に分数をその最小項に減らす必要があります。つまり、すべての演算で、基本的に最大公約数の計算を行う必要があります。
  2. 計算後、分子または分母が最も近い表現可能な結果を​​見つける必要があるために、表現できない結果が生じる場合。これはささいなことです。

一部の言語は分数タイプを提供しますが、通常は分数の精度と組み合わせてそれを行います。これにより、分数の概算について心配する必要がなくなりますが、分母のサイズの数値が多数の計算ステップを通過するときに、それ自体に問題が生じます。したがって、フラクションに必要なストレージが爆発する可能性があります。

一部の言語では10進浮動小数点型も提供されます。これらは主に、人間が考慮して作成された既存の丸め規則(主に財務計算)にコンピューターが一致する結果が得られることが重要なシナリオで使用されます。これらはバイナリ浮動小数点よりも処理が少し難しいですが、最大の問題は、ほとんどのコンピューターがハードウェアサポートを提供していないことです。


-4

これを試して

DecimalFormat decimalFormat = new DecimalFormat("#.##");
String.valueOf(decimalFormat.format(decimalValue))));

' decimalValue'は変換する値です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.