LoginSignup
0
0

[JavaScript] 整数を長さ 64 ビットの Uint8Array に変換する

Posted at

1. Number 型の場合

JavaScript の通常の数値は Number 型で、絶対値の長さが 53 ビットの整数を扱うことができます。

参考「Number.MIN_SAFE_INTEGER - JavaScript | MDN
参考「Number.MAX_SAFE_INTEGER - JavaScript | MDN

ただし、JavaScript でのビット演算は数値を 32 ビットとして扱います。

参考「バイナリービット演算子 - 式と演算子 - JavaScript | MDN

そのため、ビット演算の右シフトでなく割り算を用いて長さを 32 ビット以下の値に分割する必要があります。

/**
 * 整数をビッグエンディアン形式で Uint8Array に変換する
 */
const intToUint8ArrayInBigEndian = number => {

	// メモ: number が負の場合、Math.floor() なしだと正しい結果を得られないため注意
	const highOrderDigits = Math.floor(number / 0x1_00000000);

	// 
	const uint8Array = new Uint8Array(8);

	uint8Array[0] = highOrderDigits >>> 24 & 0xff;
	uint8Array[1] = highOrderDigits >>> 16 & 0xff;
	uint8Array[2] = highOrderDigits >>>  8 & 0xff;
	uint8Array[3] = highOrderDigits        & 0xff;

	uint8Array[4] = number >>> 24 & 0xff;
	uint8Array[5] = number >>> 16 & 0xff;
	uint8Array[6] = number >>>  8 & 0xff;
	uint8Array[7] = number        & 0xff;

	return uint8Array;

};
// 絶対値の長さが 53 ビットの正の整数
const numberA = 0x123456_789abcde;
console.log(numberA.toString(16));

const uint8ArrayA = intToUint8ArrayInBigEndian(numberA);
console.log(Array.from(uint8ArrayA, uint8 => uint8.toString(16).padStart(2, '0')).join());

// 絶対値の長さが 53 ビットの正の整数
const numberB = Number.MAX_SAFE_INTEGER;
console.log(numberB.toString(16));

const uint8ArrayB = intToUint8ArrayInBigEndian(numberB);
console.log(Array.from(uint8ArrayB, uint8 => uint8.toString(16).padStart(2, '0')).join());

// 絶対値の長さが 53 ビットの負の整数
const numberC = Number.MIN_SAFE_INTEGER;
console.log(numberC.toString(16));

const uint8ArrayC = intToUint8ArrayInBigEndian(numberC);
console.log(Array.from(uint8ArrayC, uint8 => uint8.toString(16).padStart(2, '0')).join());
例の実行結果
123456789abcde
00,12,34,56,78,9a,bc,de
1fffffffffffff
00,1f,ff,ff,ff,ff,ff,ff
-1fffffffffffff
ff,e0,00,00,00,00,00,01

ビット演算の右シフトは以下のように割り算で計算できます。

 0x7fffffff >>> 16 === 0x7fff;
-0x7fffffff >>> 16 === 0x8000;
 0x80000001 >>> 16 === 0x8000;

(Math.floor( 0x7fffffff / 0x00010000) & 0xffff) === 0x7fff;
(Math.floor(-0x7fffffff / 0x00010000) & 0xffff) === 0x8000;
(Math.floor( 0x80000001 / 0x00010000) & 0xffff) === 0x8000;

2. BigInt 型の場合

JavaScript では BigInt 型を用いることで大きな数を扱えますが、Number 型より計算に時間がかかります。

/**
 * BigInt をビッグエンディアン形式で Uint8Array に変換する
 */
const bigIntToUint8ArrayInBigEndian = bigInt => {

	const uint8Array = new Uint8Array(8);

	uint8Array[0] = Number(bigInt >> 56n & 0xffn);
	uint8Array[1] = Number(bigInt >> 48n & 0xffn);
	uint8Array[2] = Number(bigInt >> 40n & 0xffn);
	uint8Array[3] = Number(bigInt >> 32n & 0xffn);

	uint8Array[4] = Number(bigInt >> 24n & 0xffn);
	uint8Array[5] = Number(bigInt >> 16n & 0xffn);
	uint8Array[6] = Number(bigInt >>  8n & 0xffn);
	uint8Array[7] = Number(bigInt        & 0xffn);

	return uint8Array;

};
// 長さが 64 ビットの正の整数
const bigIntA = 0x12345678_9abcdef0n;
console.log(bigIntA.toString(16));

const uint8ArrayA = bigIntToUint8ArrayInBigEndian(bigIntA);
console.log(Array.from(uint8ArrayA, uint8 => uint8.toString(16).padStart(2, '0')).join());

// 長さが 64 ビットの正の整数
const bigIntB = 0x7fffffff_ffffffffn;
console.log(bigIntB.toString(16));

const uint8ArrayB = bigIntToUint8ArrayInBigEndian(bigIntB);
console.log(Array.from(uint8ArrayB, uint8 => uint8.toString(16).padStart(2, '0')).join());

// 長さが 64 ビットの負の整数
const bigIntC = -0x7fffffff_ffffffffn;
console.log(bigIntC.toString(16));

const uint8ArrayC = bigIntToUint8ArrayInBigEndian(bigIntC);
console.log(Array.from(uint8ArrayC, uint8 => uint8.toString(16).padStart(2, '0')).join());
例の実行結果
123456789abcdef0
12,34,56,78,9a,bc,de,f0
7fffffffffffffff
7f,ff,ff,ff,ff,ff,ff,ff
-7fffffffffffffff
80,00,00,00,00,00,00,01
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0