如何将两个小数合并为一个大端?
How to combine two decimals into one big-endian?
我有两个整数,例如 1511461841
,253093752
,代表自 unix 纪元以来的秒 + 纳秒。
如何将它们组合成大端 Buffer
?
1511461841253093752
太大而无法存储为数字,这使得这变得棘手并且简单地连接两个 UInt32BE 缓冲区也不正确。
(说清楚,我想编码数字1511461841253093752
,不是两个单独的整数)
1511461841253093752 的 log base 2 是 60.4,所以结果应该是 8 个字节。
我可以简单地通过执行 253093752 % 256 获得第一个(最右边的)字节,但我不知道如何在不使用 bigint 库的情况下获得第二个字节。不可能吗?
我觉得这段代码,使用了big-integer
模块,执行您的请求。我添加了一些评论来解释
发生了什么事。
// Load a module to represent big integers in javascript
var bigInt = require("big-integer");
// The value that we want to represent as big endian
var num = bigInt("1511461841253093752", 10);
// First we're going to store the bytes of this value in
// little endian order
var values = [];
while (num.compare(bigInt.zero) != 0) {
values.push(num.mod("256").valueOf());
num = num.divide("256");
}
// Fill with zeros if needed.
var desiredLength = 8;
while (values.length < desiredLength) {
values.push(0);
}
// Reverse the order to get big endian order and create
// a buffer from the array
values = values.reverse();
var buf = Buffer.from(values);
// This outputs <Buffer 14 f9 ca 89 5b 72 d1 78>
console.log(buf);
我使用 big-integer library as brm .
在 Node.js 和浏览器 JS 中实现了这个
节点
function uuid() {
const [sec,ns] = getTime();
let num = BigInt(sec + padNano(ns));
let {quotient,remainder} = num.divmod(4294967296);
let buf = Buffer.allocUnsafe(16);
buf.writeUInt32BE(quotient.valueOf(),0,true);
buf.writeUInt32BE(remainder.valueOf(),4,true);
Crypto.randomBytes(8).copy(buf, 8);
return buf;
}
function padNano(ns) {
let padLen = 9 - ns.length;
if(padLen > 0) {
return '000000000'.slice(0,padLen) + ns;
}
return String(ns);
}
浏览器
export default function uuid() {
const [sec,ns] = getTime();
let num = BigInt(sec + String(ns).padStart(9,'0'));
let {quotient,remainder} = num.divmod(4294967296);
let buf = new ArrayBuffer(16);
let dataView = new DataView(buf,0,8);
dataView.setUint32(0, quotient.valueOf(), false);
dataView.setUint32(4, remainder.valueOf(), false);
let cryptView = new Uint8Array(buf, 8, 8);
crypto.getRandomValues(cryptView);
return buf;
}
详情
正如我在问题中提到的,数字代表秒和纳秒。 1e9 纳秒 = 1 秒,所以正确的数字上限为 999999999,最多需要 30 位来表示。
我没有对问题中的 "seconds" 进行限制,但我们可以将其限制在 32 位以简化数学运算。 Unix 纪元 + 264 纳秒 = 2554 年 7 月 21 日,这对我来说已经足够了。
因此,两个 数字都非常适合 uint32。所以我们所要做的就是将 big 除以 232,将其一分为二。左半部分是商,右半部分是余数。我们可以在两个操作中做到这一点,但是大整数有一个方便的函数,它已经被称为 divmod
所以我使用了它。
接下来我们只需要使用big-endian将这两个部分写入缓冲区。幸运的是,浏览器和 Node 都有这方面的功能,所以我们只需写入两次即可完成。
我有两个整数,例如 1511461841
,253093752
,代表自 unix 纪元以来的秒 + 纳秒。
如何将它们组合成大端 Buffer
?
1511461841253093752
太大而无法存储为数字,这使得这变得棘手并且简单地连接两个 UInt32BE 缓冲区也不正确。
(说清楚,我想编码数字1511461841253093752
,不是两个单独的整数)
1511461841253093752 的 log base 2 是 60.4,所以结果应该是 8 个字节。
我可以简单地通过执行 253093752 % 256 获得第一个(最右边的)字节,但我不知道如何在不使用 bigint 库的情况下获得第二个字节。不可能吗?
我觉得这段代码,使用了big-integer 模块,执行您的请求。我添加了一些评论来解释 发生了什么事。
// Load a module to represent big integers in javascript
var bigInt = require("big-integer");
// The value that we want to represent as big endian
var num = bigInt("1511461841253093752", 10);
// First we're going to store the bytes of this value in
// little endian order
var values = [];
while (num.compare(bigInt.zero) != 0) {
values.push(num.mod("256").valueOf());
num = num.divide("256");
}
// Fill with zeros if needed.
var desiredLength = 8;
while (values.length < desiredLength) {
values.push(0);
}
// Reverse the order to get big endian order and create
// a buffer from the array
values = values.reverse();
var buf = Buffer.from(values);
// This outputs <Buffer 14 f9 ca 89 5b 72 d1 78>
console.log(buf);
我使用 big-integer library as brm
节点
function uuid() {
const [sec,ns] = getTime();
let num = BigInt(sec + padNano(ns));
let {quotient,remainder} = num.divmod(4294967296);
let buf = Buffer.allocUnsafe(16);
buf.writeUInt32BE(quotient.valueOf(),0,true);
buf.writeUInt32BE(remainder.valueOf(),4,true);
Crypto.randomBytes(8).copy(buf, 8);
return buf;
}
function padNano(ns) {
let padLen = 9 - ns.length;
if(padLen > 0) {
return '000000000'.slice(0,padLen) + ns;
}
return String(ns);
}
浏览器
export default function uuid() {
const [sec,ns] = getTime();
let num = BigInt(sec + String(ns).padStart(9,'0'));
let {quotient,remainder} = num.divmod(4294967296);
let buf = new ArrayBuffer(16);
let dataView = new DataView(buf,0,8);
dataView.setUint32(0, quotient.valueOf(), false);
dataView.setUint32(4, remainder.valueOf(), false);
let cryptView = new Uint8Array(buf, 8, 8);
crypto.getRandomValues(cryptView);
return buf;
}
详情
正如我在问题中提到的,数字代表秒和纳秒。 1e9 纳秒 = 1 秒,所以正确的数字上限为 999999999,最多需要 30 位来表示。
我没有对问题中的 "seconds" 进行限制,但我们可以将其限制在 32 位以简化数学运算。 Unix 纪元 + 264 纳秒 = 2554 年 7 月 21 日,这对我来说已经足够了。
因此,两个 数字都非常适合 uint32。所以我们所要做的就是将 big 除以 232,将其一分为二。左半部分是商,右半部分是余数。我们可以在两个操作中做到这一点,但是大整数有一个方便的函数,它已经被称为 divmod
所以我使用了它。
接下来我们只需要使用big-endian将这两个部分写入缓冲区。幸运的是,浏览器和 Node 都有这方面的功能,所以我们只需写入两次即可完成。