はじめに
JavaScriptにおける数値は、倍精度浮動小数点数しかないわけですが、倍精度浮動小数点数を構成するビット列を符号なし64ビット整数で表現した値が欲しいという場面がありました。このやり方について調べたので、記事にしてみました。
変換方法
function DoubleToBigInt(f)
{
var buf = new ArrayBuffer(8);
(new Float64Array(buf))[0] = f;
lower = BigInt((new Uint32Array(buf))[0]);
upper = BigInt((new Uint32Array(buf))[1]);
return (upper << BigInt(32)) | lower;
}
上記のコードでは、ArrayBuffer
を経由して、倍精度浮動小数点数から2つの符号なし32ビット整数に変換しています。upper
は倍精度浮動小数点数の上位32ビット、lower
は倍精度浮動小数点数の下位32ビットです。残念ながら、Uint64Array
なんてものはありませんから、こうするしかないですね…
2つの符号なし32ビット整数に変換した後は、これらの値をBitInt
に変換して符号なし64ビット整数の値を表現しています。
この関数の戻り値を確認してみましょう。
var f = 0.1234567;
console.log(DoubleToBigInt(f)); // 4593560413433027186n
4593560413433027186
という値になりましたが、これが正しいのかC言語のコードで検証してみます。
#include <stdio.h>
union {
double d;
unsigned long l;
} val;
int main(void) {
val.d = 0.1234567;
printf("%ld\n", val.l);
return 0;
}
上記のコードをコンパイルして、実行してみます。
$ gcc test.c
$ ./a.out
4593560413433027186
JavaScriptコード側の出力と一致しました。
おわりに
これをやろうと思ったのは、ももテクの記事を参考にJavaScriptのMath.random
関数(XorShift128+)の内部状態を復元しようとしたためです。浮動小数点数表現で出力されるよりは、整数値で出力された方が解析しやすいかなと思いました。
なにはともあれ、上記のコードで浮動小数点数 -> 符号なし64ビット整数への変換ができたので、一件落着です。
追記 (2020/04/05 15:38)
@uhyo さんからBigUint64Array
の存在を教えていただきました。ありがとうございます!BigUint64Array
を使えば、DoubleToBigInt
関数を以下のようにもっと簡潔に書けます。
function DoubleToBigInt(f)
{
var buf = new ArrayBuffer(8);
(new Float64Array(buf))[0] = f;
return (new BigUint64Array(buf))[0];
}
参考サイト
https://stackoverflow.com/questions/2003493/javascript-float-from-to-bits
https://qiita.com/uhyo/items/f9abb94bcc0374d7ed23