Pythonでdouble型のバイナリ表現を確認する

Pythonstructモジュールを使うと、int型やdouble型の数字をバイト列に変換したり(pack)、バイト列から数字を取り出したり(unpack)できる。 これを利用して、double型の倍精度浮動小数点数がどのようなバイト列として表現されるかを確認してみる。

一般に、double型の小数はIEEE 754のbinary64形式にて扱われる。 つまり、最初の1bitが符号を表し、続く11bitが指数、残りの52bitが仮数を表す。

実際に確かめてみる。 まずは、structモジュールをインポートして、変数に数字を代入。

$ python
Python 2.7.5 (default, Oct  2 2013, 22:34:09)
[GCC 4.8.1] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import struct
>>> x = -3.141519

xをdouble型小数としてバイト列に変換する。>dはビッグエンディアン(1バイトずつ左から右に並べる)のdouble型を意味する。

>>> xs = struct.pack('>d', x)
>>> xs
'\xc0\t!\xd4\xb6\xa6\x19\xdb'

次に、バイト列をunsigned long long型整数として解釈しなおす。 >Qはビッグエンディアンのunsigned long long型を意味する。 double型もunsigned long long型も64bitなので、ちょうど一つの値に解釈しなおすことができる。

>>> xn = struct.unpack('>Q', xs)[0]
>>> xn
13837628527553681883L
>>> bin(xn)
'0b1100000000001001001000011101010010110110101001100001100111011011'

ビット演算で各パートを取り出す。

>>> sign = (xn >> 63)
>>> sign
1L
>>> exp = (xn >> 52) & ((1<<11)-1)
>>> exp
1024L
>>> frac = xn & ((1<<52)-1)
>>> frac
2570472271518171L
>>> bin(frac)
'0b1001001000011101010010110110101001100001100111011011'

符号は1なのでマイナス、指数はバイアスの1023を引いて1である。 仮数の各ビットは2進数で表したときの小数点以下となっており、実際の仮数は次のようにして計算できる。

>>> frac2 = 1 + frac/float(1<<52)
>>> frac2
1.5707595

これらをまとめると次のようになり、これは最初に代入した数字になっている。

>>> -sign * frac2 * (2 ** (exp-1023))
-3.141519