父親が浮動小数点数を16進数に変換する方法を尋ねてきたので、色々調べてみました。
最終的にC言語とPythonで実装したので、備忘録としてまとめていきます。
父は以下のサイトで浮動小数点数を入力し、Hexadecimalの結果をExcelシートに貼り付ける操作を繰り返したそうです。
しかし、膨大なデータとなると単純作業が多くなり効率が悪いです。そこで要望の通り、まずはExcelのマクロでコンバーターを作成できないか調べてみました。
結果:無理でした。筆者のググり力では見つかりません。
これでは実装できないので、他の言語でEXEファイルを作成し、マクロで標準出力を受け取る方向性で進めていくことにします。
浮動小数点数の内部表現
IEEE754規格に準じているとか。
今回は時間もないので、流し読みで片しました。
以下参考
型変換の方針
要点だけまとめます。
という指標が立ちました。
Pythonでの16進法表現
structライブラリを利用すれば簡単に実装できます。
f = float(3.14) b = struct.pack(">f", f)
上のコードで浮動小数点数をバイト列に変換できました。ちなみに2行目の">f"
はfloat型(ビックエンディアン)の指定です。
次にこのバイト列をint型として再変換します。
i = struct.unpack(">I", b)[0]
">I"
という記述からも察することはできますが、上のコードで再変換できます。戻り値はタプルとなるので[0]
を忘れないように。
最後に16進数への変換です。hex()
関数を利用しましょう。
h = hex(i)
以上をまとめたのが以下のコードになります。
import struct import sys def float2hex(f: float) -> str: return hex(struct.unpack('>I', struct.pack('>f', f))[0]) if __name__ == '__main__': try: print(float2hex(float(sys.argv[1]))) except ValueError: print(sys.argv[1]) except IndexError: print()
先程の記述に加えて、第2引数が存在しない場合のValueErrorと数値でない場合のIndexErrorを拾ってエラー処理をしています。
C言語での16進法表現
Pythonと同じイメージで作成しようとしたらドツボにハマりました。
どうやら、共用体(union)を使えば上手くいくようです。
- float型とuint32_t型(符号なし32bit整数型)の共用体を作成
- float型の方に数値を代入
- uint32_t型で取り出して16進数で表示
共用体はデータをメモリ上で同一の場所に保管するので、メモリ上のバイト列を介して変換を行います。(過程は上の方針とほぼ同じ)
これを基に作成したのが以下のコードです。
#include <stdio.h> #include <stdint.h> #include <stdlib.h> int main(int argc, char *argv[]) { // 引数がなければエラーで終了 if (argc == 1) return -1; union { float f; uint32_t ui; } data; char *endptr; data.f = strtof(argv[1], &endptr); // 文字列->浮動小数点数 の変換に失敗した場合、エラーで終了 if (*endptr != '\0') return -2; // 16進数として出力 printf("0x%08x", data.ui); return 0; }
こちらも簡単なエラー処理は加えました。(穴はたくさんありそう。)
Excel上でマクロ作成
VBA上での外部コマンド実行及び標準出力の取得は以下のサイトを参考に作成しました。
コードは以下の通りです。
Public Sub float2hex() Dim sCmd, Result As String Dim Cell As Range Dim WS, wExec Set WS = CreateObject("WScript.Shell") ChDir ThisWorkbook.Path For Each Cell In Selection If Cell.Value = "" Then GoTo Continue End If sCmd = ".\bin\float2hex.exe" & Str(Cell.Value) Set wExec = WS.Exec("%ComSpec% /c " & sCmd) Do While wExec.Status = 0 DoEvents Loop Result = wExec.StdOut.ReadAll Cell.Value = Replace(Result, vbLf, "") Continue: Next Cell End Sub
上のマクロでは選択セルの数値をC言語で作成したコンバーターを介して16進法表記に変換します。
また、実行するファイルのパスに依存しないようにこのマクロを含むブックからの相対パスでEXEファイルを指定しています。
公開
コード及びブックはGitHubで公開しています。
方法がすぐに見つからなかったため、EXEファイルも梱包してありますが、あまりよろしくないと思うのでReleaseの方法が分かり次第変更する予定です。
コメント