• トップ
  • ブログ一覧
  • 浮動小数点について調べてみた
  • 浮動小数点について調べてみた

    はじめに

    こんにちは!

    今回は結構ややこしい浮動小数点数について書こうと思います。

    普通に計算すると以下のようになります。
    0.1 + 0.2 = 0.3
    一方でだいたいのプログラミング言語で以下の計算をすると
    0.1 + 0.2 => 0.30000000000000004
    のように表示されます。
    今回はこのなぜ?についてなるべくわかりやすく説明したいと思います。少しでも理解が深まれば幸いです。

    コンピュータではどのように計算するか

    ご存知のようにコンピュータはデータを2進数で扱います。

    4(10)=100(2)4_{(10)} = 100_{(2)}

    5(10)=101(2)5_{(10)} = 101_{(2)}

    100(2)+101(2)=1001(2)=9(10)100_{(2)} + 101_{(2)} = 1001_{(2)} = 9_{(10)}

    それはコンピュータの特性として演算は論理演算に回帰するということが理由になります(深入りしません)。そのために2進数で表現されます。

    小数の計算

    ここで、一旦分数での計算を考えてみます。

    13+23=1 \dfrac{1}{3} + \dfrac{2}{3} =1

    ここに「縛り」を加え、分数は使用できない、小数点以下4桁で切り捨て、という条件を加えます。

    すると以下のようになります。

    0.3333+0.6666=0.99990.3333 + 0.6666 = 0.9999

    このような縛りがコンピュータでも発生します(データを有限ビットで表現しなくてはならない)。

    2進数小数の計算

    小数でない場合

    4(10)=1(10)×2(10)2+0(10)×2(10)1+0(10)×2(10)04_{(10)} = 1_{(10)} \times2^2_{(10)} + 0_{(10)} \times2_{(10)} ^1 + 0_{(10)} \times2_{(10)} ^0

    2進数にするために係数をひろいます。

    4(10)=100(2)4_{(10)} = 100_{(2)}

    少数の場合も同様に以下のように分解します。

    0.5(10)=0(10)×2(10)0+1(10)×2(10)10.5_{(10)} = 0_{(10)} \times2^0_{(10)} + 1_{(10)} \times2_{(10)} ^{-1}

    2進数にするために係数をひろいます。

    0.5(10)=0.1(2)0.5_{(10)} = 0.1_{(2)}

    しかし、2進数の場合は以下のようになります。

    2進数表現10進数分数10進数表現
    212^{-1}0.112\dfrac{1}{2}0.5
    222^{-2}0.0114\dfrac{1}{4}0.25
    232^{-3}0.00118\dfrac{1}{8}0.125
    242^{-4}0.0001116\dfrac{1}{16}0.0625
    252^{-5}0.00001132\dfrac{1}{32}0.03125
    262^{-6}0.000001164\dfrac{1}{64}0.015265
    272^{-7}0.00000011128\dfrac{1}{128}0.0078125
    282^{-8}0.000000011256\dfrac{1}{256}0.00390625

    ここで0.1(10)0.1_{(10)}を2進数で表現すると循環小数となります。(変換の計算式は調べるとすぐにヒットします)

    0.1(10)=0.000110(2)0.1_{(10)} = 0.000110\cdots_{(2)}

    同様に0.2(10)0.2_{(10)}を2進数で表現すると循環小数となります。

    0.2(10)=0.00110(2)0.2_{(10)} = 0.00110\cdots_{(2)}

    上記の和は以下のような循環小数となります。

    0.3(10)=0.1(10)+0.2(10)=0.01001100(2)0.3_{(10)} = 0.1_{(10)} + 0.2_{(10)} = 0.01001100\cdots_{(2)}

    コンピュータは有限ビットでデータを保持するので、誤差が発生し、最初の解になるのでした。

    0.1 + 0.2 => 0.30000000000000004

    浮動小数点とは

    上記のように「コンピュータは2進数で演算を行う」「データは有限のビットで扱わなければならない」「2進数の小数は循環小数で多くが表現される」という制約があります。そこで用いられるのが浮動小数点数で表現する形式です。

    浮動小数点数はコンピュータで小数を表現するためにルール(IEEEなどの団体)を策定して利用されてます。

    一般的に用いられる倍精度浮動小数点数の内部構造は以下のようになります。

    その名の通り「符号部」「指数部」「仮数部」で小数を表現します(符号部は簡単なので省く)。

    この際に「指数部」、「仮数部」ではそれぞれルールがあり、ルールに基づいて正規化を行います。

    例えば8.5(10)8.5_{(10)}は2進数で以下のように表現できます。

    1000.1×20 1000.1 \times2^{0}

    100.01×21 100.01 \times2^{1}

    10.001×22 10.001 \times2^{2}

    1.0001×23 1.0001 \times2^{3}

    これを「仮数」は1仮数<21\leqq仮数\lt2の条件にすることで、以下の1通りになります。

    1.0001×23 1.0001 \times2^{3}

    1の部分は暗黙の「1」として格納されず、仮数部には小数部分のみが格納されます。

    したがって、仮数部に格納されるビット(52ビット)は以下となります。

    0001000000000000000000000000000000000000000000000000

    また「指数部」は11ビット、またバイアスは1023を用いるルールを適用します。

    バイアス(オフセット)は非負で表現するために加えます。ですので指数の表現は1022-1022から10231023まで表現できます。

    例えば指数部をE(2)E_{(2)}、今回正規化したい指数部分を3(10)=11(2)3_{(10)} = 11_{(2)}とすると、

    E(10)=1023(10)3(10)=1020(10)E_{(10)} = 1023_{(10)} - 3_{(10)} = 1020_{(10)}

    E(2)=1111111111120000000011(2)=1111111100(2)E_{(2)} = 11111111111_{2} - 0000000011_{(2)} = 1111111100_{(2)}

    したがって、指数部に格納されるビット(11ビット)は以下となります。

    1111111100

    まとめ

    今回は浮動小数点についてふれてみました。

    結構データ構造が複雑ですよね。

    また、上記のような制約があるので、コンピュータの計算では「丸め誤差」「情報落ち」「桁落ち」などが発生します。

    ぜひ深く学ぶきっかけにしてみてください。

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    けったん(エンジニア)
    けったん(エンジニア)
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background