Pythonで小数を2進数に変換する

Pythonで1.23のような整数部のある小数を2進数に変換するときには、整数部、小数部の順に計算します。浮動小数点方式では仮数部として53桁を使いますが、まずは整数部を2進数に変換し、余った桁数を使い、小数部を表すようにします。

計算するための関数の作成

整数部のある小数を2進数に変換するため計算をするために、細かい機能を果たす関数を作成します。1.1では整数部の2進数への変換、1.2では小数部の2進数への計算、1.3では割り切れなかった時の丸めの計算をする関数になりあます。次項でこれらの関数を使い関数を定義し、正しく計算できることを検証します。

整数部の計算

まず、整数部を計算する関数を作成します。

  1. # 1 10進数から2進数に変換(整数)
  2. def dec2bin_int(dec):
  3. Bin = ''
  4. while dec:
  5. Bin = str(dec % 2) + Bin
  6. dec //= 2
  7. bias = len(Bin)-1
  8. return Bin, bias
  9. dec2bin_int(10)

('1010', 3)

2. 引数decは2進数に変換する整数を受け取ります。
3. 2進数に変換後の値を計算するため、変数Binを文字列として定義します。
4. while decとはdecが0でない間、繰り返すことを示します。この間、次々とdecを2倍しながら整数を2進数に変換していきます。
7. 整数を2進数に変換した場合、初めの桁が1となり、2桁目以降が指数部の桁数となるため、Binの長さから1を差し引いた数値がbiasとなります。

小数部の計算

小数の部分の計算で求める桁数は、53-整数部分の桁数となるため、変換する必要がある1未満の小数(f)と桁数(digits)を引数として指定します。関数の戻り値は整数分部のあとにつなげることになるので、biasを考慮する必要はありませんが、最終的に丸め処理をする必要があるので、割り切れない場合にはそのことをrbとして戻す必要があります。

  1. # 2 10進数から2進数に変換(小数)
  2. from decimal import Decimal
  3. def dec2bin_decimal(f, digits):
  4. dec = Decimal(str(f))
  5. Bin = ''
  6. for i in range(digits):
  7. dec *= 2
  8. if dec >= 1:
  9. Bin += '1'
  10. dec = dec - 1
  11. else:
  12. Bin += '0'
  13. if dec != 0:
  14. rb = True
  15. else:
  16. rb = False
  17. return Bin, rb
  18. dec2bin_decimal(0.1, 53)

('00011001100110011001100110011001100110011001100110011', True)

4. 引数で渡された小数を2進数に正しく計算するために、Decimal関数を使い、変数decに代入します。
6. for ループを使い変数deigitsで指定した桁数だけ2進数に変換し、変数Binに代入します。
13. digitsで指定した桁数だけ計算しても割り切れずdecに残が残っている場合、変数rbをTrueとします。

2進数の文字列の丸め処理をする

2進数に変換したのち、指定した桁数まで計算しても割り切れない場合、2進数の丸め処理を行う関数です。

  1. # 3 2進数の丸め
  2. def round_bin(Bin, rb): # 1桁で判断 決定版
  3. ulp = Bin[-2] # unit in the last place
  4. gb = Bin[-1] # Guard bit
  5. sw_up = False
  6. if gb == '1':
  7. if rb == True: # Round bit
  8. sw_up = True
  9. else:
  10. if ulp == '1':
  11. sw_up = True
  12. result = Bin[:-1]
  13. if sw_up == True:
  14. return bin(eval('0b'+result) + eval('0b'+(len(Bin)-2)*'0'+'1'))[2:]
  15. else:
  16. return result

詳しくは次をご覧ください

整数部のある小数を2進数に変換し、確認する

2進数に変換する関数

整数部がある小数を2進数に変換する簡単な方法として、整数部(i)と小数部(f)に分けて計算します。

  1. # 4 10進数から2進数への変換(整数+小数)
  2. from decimal import Decimal
  3. def dec2bin(num):
  4. i = int(num)
  5. f = float(Decimal(str(num))-Decimal(str(i)))
  6. ib, bias = dec2bin_int(i)
  7. fb, rb = dec2bin_decimal(f, 53-bias)
  8. return round_bin(ib+fb, rb), bias
  9. print(hex2bin(10.6) )
  10. print(hex2bin(3.14) )

('10101001100110011001100110011001100110011001100110011', 3)
('11001000111101011100001010001111010111000010100011111', 1)

4. 整数部はint関数を使い整数部を取り出し、変数iに代入します。
5. 渡された数値から4.の整数部の値を差し引き、小数部を計算します。このとき正確を期すために、Decimal関数で計算したのちfloat関数で小数に戻します。
6. dec2bin関数で整数部の仮数部の文字列をib、指数部のバイアスをbiasとして計算します。biasはibの文字数-1になります。
7. dec2bin_decimal関数で、小数部分を2進数に変換します。引数のdigitsを53-biasとすることで53より1だけ多い桁数を指定します。
8. 6.で計算した整数部と7.で計算した小数部を結合した54桁の文字列、およびrbの値を使い丸め処理をして仮数部を計算します。また、dec2bin関数で計算した指数部の値をそのまま戻り値として使います。

上記の方法により、整数部分がある小数を2進数に変換することができました。

2進数に変換する関数を検証する

小数を2進数に変換するbin_3関数が正しいのか検証してみます。まず、Pythonのhexメソッドを通して2進数に変換するhex2bin関数を作成します。

  1. #5 16進数から2進数への変換
  2. def hex2bin(h):
  3. h=h.hex()
  4. Bin='1'
  5. for i in range(4,17):
  6. Bin+=(format(int(h[i],16),'#06b')[2:6])
  7. return Bin,int(h[19:])
  8. print(hex2bin(10.6) )
  9. print(hex2bin(3.14) )

('10101001100110011001100110011001100110011001100110011', 3)
('11001000111101011100001010001111010111000010100011111', 1)

こちらは少なくとも16進数に変換するまではhexメソッドを使っているので正しく、2進数に変換する部分はそんなに複雑なことはしていないので正しく変換できているものと思われます。

  1. #6 確認
  2. for i in range(1,100):
  3. for j in range(10):
  4. for k in range(10):
  5. for l in range(10):
  6. x=i+j/10+k/100+l/1000
  7. if dec2bin(x)!=hex2bin(x):
  8. print(x,dec_bin_c(x),hex2bin(x))

1つも出力されないので、正しく計算されたことがわかりました。