如何使用 "table" 优化在 JavaScript 中反转 32 位?
How to reverse 32-bits in JavaScript using the "table" optimization?
所以我终于想通了how to reverse 8-bits, 16-bits, and possibly 32-bits。然而,它似乎在 JavaScript 的 32 位情况下不起作用。在 JavaScript 中,您如何使用优化的基于 table 的方法做到这一点?
Table 32 位解决方案
const BIT_REVERSAL_TABLE = new Array(256)
for (var i = 0; i < 256; ++i) {
var v = i, r = i, s = 7;
for (v >>>= 1; v; v >>>= 1) {
r <<= 1;
r |= v & 1;
--s;
}
BIT_REVERSAL_TABLE[i] = (r << s) & 0xff;
}
function reverseBits32(n) {
return (BIT_REVERSAL_TABLE[n & 0xff] << 24) |
(BIT_REVERSAL_TABLE[(n >>> 8) & 0xff] << 16) |
(BIT_REVERSAL_TABLE[(n >>> 16) & 0xff] << 8) |
BIT_REVERSAL_TABLE[(n >>> 24) & 0xff];
}
log32(0b11110010111110111100110010101011)
function log32(n) {
console.log(`${bits(n, 32)} => ${bits(reverseBits32(n), 32)}`)
}
function bits(n, size) {
return `0b${n.toString(2).padStart(size, '0')}`
}
注意日志说:
0b11110010111110111100110010101011 => 0b0-101010110011000010000010110001
什么时候应该说:
0b11110010111110111100110010101011 => 0b11010101001100111101111101001111
非table 32 位解决方案(不起作用)
// brev_knuth
function reverseBits32(a) {
let t
a = (a << 15) | (a >> 17);
t = (a ^ (a >> 10)) & 0x003f801f;
a = (t + (t << 10)) ^ a;
t = (a ^ (a >> 4)) & 0x0e038421;
a = (t + (t << 4)) ^ a;
t = (a ^ (a >> 2)) & 0x22488842;
a = (t + (t << 2)) ^ a;
return a;
}
function reverseBits32(x) {
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
return((x >> 16) | (x << 16));
}
我认为这些可能不起作用,因为移位会创建大于 32 位的数字?不确定。任何提示将不胜感激。
非table 32 位解决方案(有效)
function reverseBits32(n) {
let res = 0;
for (let i = 0; i < 32; i++) {
res = (res << 1) + (n & 1);
n = n >>> 1;
}
return res >>> 0;
}
log32(0b11110010111110111100110010101011)
function log32(n) {
console.log(`${bits(n, 32)} => ${bits(reverseBits32(n), 32)}`)
}
function bits(n, size) {
return `0b${n.toString(2).padStart(size, '0')}`
}
首先,为什么我的 32 位 table 解决方案不起作用?那么,如何在 32 位整数上使用 JavaScript 中的 table 方法(不使用 BigInt,并且不使用“转换为字符串和反向”技巧)来完成这项工作?
我会注意到 JavaScript 中的这个“BigInt 64”版本有效,所以是的。
function reverseBits64(n) {
return (BigInt(BIT_REVERSAL_TABLE[Number(n & 255n)]) << 56n) |
(BigInt(BIT_REVERSAL_TABLE[Number((n >> 8n) & 255n)]) << 48n) |
(BigInt(BIT_REVERSAL_TABLE[Number((n >> 16n) & 255n)]) << 40n) |
(BigInt(BIT_REVERSAL_TABLE[Number((n >> 24n) & 255n)]) << 32n) |
(BigInt(BIT_REVERSAL_TABLE[Number((n >> 32n) & 255n)]) << 24n) |
(BigInt(BIT_REVERSAL_TABLE[Number((n >> 40n) & 255n)]) << 16n) |
(BigInt(BIT_REVERSAL_TABLE[Number((n >> 48n) & 255n)]) << 8n) |
BigInt(BIT_REVERSAL_TABLE[Number((n >> 56n) & 255n)]);
}
// 0b1111001011111011110011001010101111110010111110111100110010101011 =>
// 0b1101010100110011110111110100111111010101001100111101111101001111
问题是在您的 reverseBits32 中,有时,带符号的 32 位数字是负数!喜欢,一半时间
一种处理方法是使用 BigInt
const reverseBits32= (x) => {
x = BigInt(x);
x = (((x & 0xaaaaaaaan) >> 1n) | ((x & 0x55555555n) << 1n));
x = (((x & 0xccccccccn) >> 2n) | ((x & 0x33333333n) << 2n));
x = (((x & 0xf0f0f0f0n) >> 4n) | ((x & 0x0f0f0f0fn) << 4n));
x = (((x & 0xff00ff00n) >> 8n) | ((x & 0x00ff00ffn) << 8n));
x = ((x >> 16n) | (x << 16n)) & 0xffffffffn;
return Number(x);
}
现在您可以随心所欲地使用位操作了
您会发现其他 non-working 函数有类似的解决方案
Table 基于解决方案
const BIT_REVERSAL_TABLE = new Array(256)
for (var i = 0; i < 256; ++i) {
var v = i, r = i, s = 7;
for (v >>>= 1; v; v >>>= 1) {
r <<= 1;
r |= v & 1;
--s;
}
BIT_REVERSAL_TABLE[i] = (r << s) & 0xff;
}
function reverseBits32(n) {
return (BIT_REVERSAL_TABLE[n & 0xff] * 2**24) +
(BIT_REVERSAL_TABLE[(n >>> 8) & 0xff] * 2 **16) +
(BIT_REVERSAL_TABLE[(n >>> 16) & 0xff] * 2 **8) +
BIT_REVERSAL_TABLE[(n >>> 24) & 0xff];
}
log32(0b11110010111110111100110010101011)
function log32(n) {
console.log(`${bits(n, 32)} => ${bits(reverseBits32(n), 32)}`)
}
function bits(n, size) {
return `0b${n.toString(2).padStart(size, '0')}`
}
所以我终于想通了how to reverse 8-bits, 16-bits, and possibly 32-bits。然而,它似乎在 JavaScript 的 32 位情况下不起作用。在 JavaScript 中,您如何使用优化的基于 table 的方法做到这一点?
Table 32 位解决方案
const BIT_REVERSAL_TABLE = new Array(256)
for (var i = 0; i < 256; ++i) {
var v = i, r = i, s = 7;
for (v >>>= 1; v; v >>>= 1) {
r <<= 1;
r |= v & 1;
--s;
}
BIT_REVERSAL_TABLE[i] = (r << s) & 0xff;
}
function reverseBits32(n) {
return (BIT_REVERSAL_TABLE[n & 0xff] << 24) |
(BIT_REVERSAL_TABLE[(n >>> 8) & 0xff] << 16) |
(BIT_REVERSAL_TABLE[(n >>> 16) & 0xff] << 8) |
BIT_REVERSAL_TABLE[(n >>> 24) & 0xff];
}
log32(0b11110010111110111100110010101011)
function log32(n) {
console.log(`${bits(n, 32)} => ${bits(reverseBits32(n), 32)}`)
}
function bits(n, size) {
return `0b${n.toString(2).padStart(size, '0')}`
}
注意日志说:
0b11110010111110111100110010101011 => 0b0-101010110011000010000010110001
什么时候应该说:
0b11110010111110111100110010101011 => 0b11010101001100111101111101001111
非table 32 位解决方案(不起作用)
// brev_knuth
function reverseBits32(a) {
let t
a = (a << 15) | (a >> 17);
t = (a ^ (a >> 10)) & 0x003f801f;
a = (t + (t << 10)) ^ a;
t = (a ^ (a >> 4)) & 0x0e038421;
a = (t + (t << 4)) ^ a;
t = (a ^ (a >> 2)) & 0x22488842;
a = (t + (t << 2)) ^ a;
return a;
}
function reverseBits32(x) {
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
return((x >> 16) | (x << 16));
}
我认为这些可能不起作用,因为移位会创建大于 32 位的数字?不确定。任何提示将不胜感激。
非table 32 位解决方案(有效)
function reverseBits32(n) {
let res = 0;
for (let i = 0; i < 32; i++) {
res = (res << 1) + (n & 1);
n = n >>> 1;
}
return res >>> 0;
}
log32(0b11110010111110111100110010101011)
function log32(n) {
console.log(`${bits(n, 32)} => ${bits(reverseBits32(n), 32)}`)
}
function bits(n, size) {
return `0b${n.toString(2).padStart(size, '0')}`
}
首先,为什么我的 32 位 table 解决方案不起作用?那么,如何在 32 位整数上使用 JavaScript 中的 table 方法(不使用 BigInt,并且不使用“转换为字符串和反向”技巧)来完成这项工作?
我会注意到 JavaScript 中的这个“BigInt 64”版本有效,所以是的。
function reverseBits64(n) {
return (BigInt(BIT_REVERSAL_TABLE[Number(n & 255n)]) << 56n) |
(BigInt(BIT_REVERSAL_TABLE[Number((n >> 8n) & 255n)]) << 48n) |
(BigInt(BIT_REVERSAL_TABLE[Number((n >> 16n) & 255n)]) << 40n) |
(BigInt(BIT_REVERSAL_TABLE[Number((n >> 24n) & 255n)]) << 32n) |
(BigInt(BIT_REVERSAL_TABLE[Number((n >> 32n) & 255n)]) << 24n) |
(BigInt(BIT_REVERSAL_TABLE[Number((n >> 40n) & 255n)]) << 16n) |
(BigInt(BIT_REVERSAL_TABLE[Number((n >> 48n) & 255n)]) << 8n) |
BigInt(BIT_REVERSAL_TABLE[Number((n >> 56n) & 255n)]);
}
// 0b1111001011111011110011001010101111110010111110111100110010101011 =>
// 0b1101010100110011110111110100111111010101001100111101111101001111
问题是在您的 reverseBits32 中,有时,带符号的 32 位数字是负数!喜欢,一半时间
一种处理方法是使用 BigInt
const reverseBits32= (x) => {
x = BigInt(x);
x = (((x & 0xaaaaaaaan) >> 1n) | ((x & 0x55555555n) << 1n));
x = (((x & 0xccccccccn) >> 2n) | ((x & 0x33333333n) << 2n));
x = (((x & 0xf0f0f0f0n) >> 4n) | ((x & 0x0f0f0f0fn) << 4n));
x = (((x & 0xff00ff00n) >> 8n) | ((x & 0x00ff00ffn) << 8n));
x = ((x >> 16n) | (x << 16n)) & 0xffffffffn;
return Number(x);
}
现在您可以随心所欲地使用位操作了
您会发现其他 non-working 函数有类似的解决方案
Table 基于解决方案
const BIT_REVERSAL_TABLE = new Array(256)
for (var i = 0; i < 256; ++i) {
var v = i, r = i, s = 7;
for (v >>>= 1; v; v >>>= 1) {
r <<= 1;
r |= v & 1;
--s;
}
BIT_REVERSAL_TABLE[i] = (r << s) & 0xff;
}
function reverseBits32(n) {
return (BIT_REVERSAL_TABLE[n & 0xff] * 2**24) +
(BIT_REVERSAL_TABLE[(n >>> 8) & 0xff] * 2 **16) +
(BIT_REVERSAL_TABLE[(n >>> 16) & 0xff] * 2 **8) +
BIT_REVERSAL_TABLE[(n >>> 24) & 0xff];
}
log32(0b11110010111110111100110010101011)
function log32(n) {
console.log(`${bits(n, 32)} => ${bits(reverseBits32(n), 32)}`)
}
function bits(n, size) {
return `0b${n.toString(2).padStart(size, '0')}`
}