获取数字的重要一半
Getting the significant half of a Number
我正在尝试将值小于或等于 Number.MAX_SAFE_INTEGER 的数字分解为两个 32 位 uint 值。
我对获取高值的第一个想法就是值 >>> 32 但这首先将值转换为 32b uint,因此结果为 0。
另一个想法是
var high = Number(BigInt(value) >> 32n);
但是,使用 BigInt 相对较慢,我想尽可能避免这种情况
然后我尝试了 Math.floor(value / 2 ** 32) ,它比 BigInt 快得多,而且看起来很准确,但我需要保证它是准确的。如果该值介于 2 ** 32 和 Number.MAX_SAFE_INTEGER 之间,是否有任何不准确的可能性?这是一个浮点计算,所以我很怀疑。
还有其他快速获取 53b 整数高值的方法吗?
编辑:如果您的计算机速度非常快,可以测试第三种方法
for (let v = 0, high = 0; high <= 2 ** 21; high++) {
for (let low = 0; low < 2 ** 32; low++) {
if (Math.trunc(v++ / 2 ** 32) != high) throw "Error at " + v;
}
}
在我的笔记本电脑上,这需要 6 年半的时间。
提取位
鉴于value
是一个整数,其大小不超过Number.MAX_SAFE_INTEGER
(253−1),则二进制数的低32位对于 value
可以通过以下方式获得:
value % 4294967296
高位可以通过以下方式获得:
Math.trunc(value / 4294967296)
(4,294,967,296 是 232。由于我不经常使用 JavaScript,所以我不会对在源代码中编写它的最佳方式发表意见。当然 4294967296
是正确的,但它并不意味着它是 232 的事实。(1<<32)
可能是一个选项。)
请注意,如果 value
为正,则通过这种方式获得的结果将为正(或零),如果 value
为负,则为负。如果 value
为负时需要不同的东西,则必须进行一些调整。
理由
JavaScript 是 ECMAScript 的一个实现。对于此处的参考,我使用 the 2020 specification of ECMA-262.
Number
类型上的%
操作符提供了Number::remainder
操作(6.1.6,Table2),具体在6.1.6.1.6 .对于手头的情况(有限股息和除数,除数非零):
… the floating-point remainder r from a dividend n and a divisor d is defined by the mathematical relation r = n - (d × q) where q is an integer that is negative only if n/d is negative and positive only if n/d is positive, and whose magnitude is as large as possible without exceeding the magnitude of the true mathematical quotient of n and d. r is computed and rounded to the nearest representable value using IEEE 754-2019 roundTiesToEven mode.
关于取整的文字对于余数运算来说是多余的。由于 r 的大小不能大于 n 或 d 中较小的一个,它表示为Number
格式至少和 each 一样精细,因此 Number
格式能够准确表示 r。
所以 value % 4294967296
准确地给出了 value
的低 32 位。
在Math.trunc(value / 4294967296)
中,除法是二的幂。在Number
格式中,value
表示为f•2e,对于一些有效数f(这里包括符号)和一些指数e。那么value / 4294967296
的数学结果就是f•2e−32。由于value
是一个整数,e远离Number
格式的指数下界(−1022),e−32 也离它很远,所以没有发生下溢。也就是说f•2e−32是可以精确表示的,所以计算时没有舍入误差value / 4294967296
。然后 Math.trunc
取整数部分,产生 value
的高位,没有错误。
我正在尝试将值小于或等于 Number.MAX_SAFE_INTEGER 的数字分解为两个 32 位 uint 值。
我对获取高值的第一个想法就是值 >>> 32 但这首先将值转换为 32b uint,因此结果为 0。
另一个想法是
var high = Number(BigInt(value) >> 32n);
但是,使用 BigInt 相对较慢,我想尽可能避免这种情况
然后我尝试了 Math.floor(value / 2 ** 32) ,它比 BigInt 快得多,而且看起来很准确,但我需要保证它是准确的。如果该值介于 2 ** 32 和 Number.MAX_SAFE_INTEGER 之间,是否有任何不准确的可能性?这是一个浮点计算,所以我很怀疑。
还有其他快速获取 53b 整数高值的方法吗?
编辑:如果您的计算机速度非常快,可以测试第三种方法
for (let v = 0, high = 0; high <= 2 ** 21; high++) {
for (let low = 0; low < 2 ** 32; low++) {
if (Math.trunc(v++ / 2 ** 32) != high) throw "Error at " + v;
}
}
在我的笔记本电脑上,这需要 6 年半的时间。
提取位
鉴于value
是一个整数,其大小不超过Number.MAX_SAFE_INTEGER
(253−1),则二进制数的低32位对于 value
可以通过以下方式获得:
value % 4294967296
高位可以通过以下方式获得:
Math.trunc(value / 4294967296)
(4,294,967,296 是 232。由于我不经常使用 JavaScript,所以我不会对在源代码中编写它的最佳方式发表意见。当然 4294967296
是正确的,但它并不意味着它是 232 的事实。(1<<32)
可能是一个选项。)
请注意,如果 value
为正,则通过这种方式获得的结果将为正(或零),如果 value
为负,则为负。如果 value
为负时需要不同的东西,则必须进行一些调整。
理由
JavaScript 是 ECMAScript 的一个实现。对于此处的参考,我使用 the 2020 specification of ECMA-262.
Number
类型上的%
操作符提供了Number::remainder
操作(6.1.6,Table2),具体在6.1.6.1.6 .对于手头的情况(有限股息和除数,除数非零):
… the floating-point remainder r from a dividend n and a divisor d is defined by the mathematical relation r = n - (d × q) where q is an integer that is negative only if n/d is negative and positive only if n/d is positive, and whose magnitude is as large as possible without exceeding the magnitude of the true mathematical quotient of n and d. r is computed and rounded to the nearest representable value using IEEE 754-2019 roundTiesToEven mode.
关于取整的文字对于余数运算来说是多余的。由于 r 的大小不能大于 n 或 d 中较小的一个,它表示为Number
格式至少和 each 一样精细,因此 Number
格式能够准确表示 r。
所以 value % 4294967296
准确地给出了 value
的低 32 位。
在Math.trunc(value / 4294967296)
中,除法是二的幂。在Number
格式中,value
表示为f•2e,对于一些有效数f(这里包括符号)和一些指数e。那么value / 4294967296
的数学结果就是f•2e−32。由于value
是一个整数,e远离Number
格式的指数下界(−1022),e−32 也离它很远,所以没有发生下溢。也就是说f•2e−32是可以精确表示的,所以计算时没有舍入误差value / 4294967296
。然后 Math.trunc
取整数部分,产生 value
的高位,没有错误。