如何将两个小数合并为一个大端?

How to combine two decimals into one big-endian?

我有两个整数,例如 1511461841253093752,代表自 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 都有这方面的功能,所以我们只需写入两次即可完成。