关于javascript:如何从八(8)个4位整数创建一个32位整数?

How to create a 32-bit integer from eight (8) 4-bit integers?

假设我有一个最大32位整数-

1
2
3
4
5
6
7
8
9
10
11
12
13
const a =
  ((2 ** 32) - 1)
 
const b =
  parseInt("11111111111111111111111111111111", 2) // 32 bits, each is a one!
 
console.log(a === b) // true

console.log(a.toString(2))
// 11111111111111111111111111111111  (32 ones)

console.log(b.toString(2))
// 11111111111111111111111111111111  (32 ones)

到现在为止,一直都还不错。但是现在假设我想用八(8)个4位数字来生成一个32位数字。这个想法很简单:将(<<每个4位序列移位到适当的位置,并将(+它们加在一起-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const make = ([ bit, ...more ], e = 0) =>
  bit === undefined
    ? 0
    : (bit << e) + make (more, e + 4)

const print = n =>
  console.log(n.toString(2))

// 4 bits
print(make([ 15 ])) // 1111

// 8 bits
print(make([ 15, 15 ])) // 11111111

// 12 bits
print(make([ 15, 15, 15 ])) // 111111111111

// 16 bits
print(make([ 15, 15, 15, 15 ])) // 1111111111111111

// 20 bits
print(make([ 15, 15, 15, 15, 15 ])) // 11111111111111111111

// 24 bits
print(make([ 15, 15, 15, 15, 15, 15 ])) // 111111111111111111111111

// 28 bits
print(make([ 15, 15, 15, 15, 15, 15, 15 ])) // 1111111111111111111111111111

// almost there ... now 32 bits
print(make([ 15, 15, 15, 15, 15, 15, 15, 15 ])) // -1 :(

我得到的是-1,但是预期的结果是所有的32位,或者说11111111111111111111111111111111

更糟糕的是,如果我从预期的结果开始,然后逆向工作,我就会得到预期的结果。-

1
2
3
4
5
6
7
8
9
const c =
 `11111111111111111111111111111111`

const d =
  parseInt(c, 2)
 
console.log(d) // 4294967295

console.log(d.toString(2) === c) // true

我试着调试make函数以确保没有明显的问题-

1
2
3
4
5
6
7
const make = ([ bit, ...more ], e = 0) =>
  bit === undefined
    ? `0`
    : `(${bit} << ${e}) + ` + make (more, e + 4)

console.log(make([ 15, 15, 15, 15, 15, 15, 15, 15 ]))
// (15 << 0) + (15 << 4) + (15 << 8) + (15 << 12) + (15 << 16) + (15 << 20) + (15 << 24) + (15 << 28) + 0

这个公式看起来像是检验出来的。我想这可能与+有关,然后切换到按位或(|应该在这里有效地做同样的事情。-

1
2
3
4
5
6
7
8
9
10
11
12
const a =
  parseInt("1111",2)
 
const b =
  (a << 0) | (a << 4)
 
console.log(b.toString(2)) // 11111111

const c =
  b | (a << 8)
 
console.log(c.toString(2)) // 111111111111

但是,当我尝试合并所有八(8)个数字时,我的make函数也会遇到同样的错误。-

1
2
3
4
5
6
7
8
9
10
11
12
const make = ([ bit, ...more ], e = 0) =>
  bit === undefined
    ? 0
    : (bit << e) | make (more, e + 4)

const print = n =>
  console.log(n.toString(2))


print(make([ 15, 15, 15, 15, 15, 15, 15 ])) // 1111111111111111111111111111 (28 bits)

print(make([ 15, 15, 15, 15, 15, 15, 15, 15 ])) // -1 :(

什么给予?

目标是使用javascript将八(8)个4位整数转换为单个32位整数-这只是我的尝试。我很好奇我的功能在哪里被破坏,但是我愿意接受其他的解决方案。

我希望避免将每个4位整数转换为二进制字符串,将二进制字符串混合在一起,然后将二进制字符串解析为单个int。建议使用数值解决方案。


位运算符将产生有符号的32位数字,这意味着如果位置31处的位(从右边最低有效位(即位0)算起)为1,则该数字将为负数。

为了避免这种情况发生,请使用除<<|以外的其他运算符,这两个运算符都会产生有符号的32位数字。例如:

1
(bit * 2**e) + make (more, e + 4)

强制无符号32位

位移位运算符旨在强制结果进入有符号32位范围,至少在MDN上声明(写入时):

The operands of all bitwise operators are converted to signed 32-bit integers

事实上,这并不完全正确。>>>运算符是一个例外。ECMAScript 2015第12.5.8.1节指出,操作数在0位移位之前映射到无符号32位。所以即使你要移动零位,你也会看到这种效果。

您只需对最终值应用一次,例如在您的print函数中:

1
console.log((n>>>0).toString(2))

号Bigint解决方案

如果您甚至需要超过32位,并且您的javascript引擎支持bigint,就像一些已经支持的那样,那么对位运算符中涉及的操作数使用bigints——然后这些操作数将不使用32位有符号数字包装(注意n后缀):

1
2
3
4
5
6
7
8
9
10
11
12
const make = ([ bit, ...more ], e = 0n) =>
  bit === undefined
    ? 0n
    : (bit << e) + make (more, e + 4n)

const print = n =>
  console.log(n.toString(2))

// Test
for (let i=1; i<20; i++) {
    print(make(Array(i).fill(15n))) // longer and longer array...
}

注意:如果你在运行上述程序时出错,请用chrome再试一次…