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

问题描述 投票:14回答:1

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

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位序列移位(<<)到位并将(+)加在一起 ​​-

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

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

const c =
 `11111111111111111111111111111111`

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

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

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

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

该公式看起来像是检查出来的。我想也许这与+有关,并切换到按位或(|),这应该在这里有效地做同样的事情 -

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函数相同的错误 -

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。数字解决方案是首选。

javascript twos-complement base-conversion
1个回答
13
投票

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

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

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

Forcing unsigned 32-bit

位移操作符旨在将结果强制转换为带符号的32位范围,至少在mdn上声明(在撰写本文时):

所有位运算符的操作数都转换为带符号的32位整数

事实上这并非完全正确。 >>>运算符是一个例外。 EcmaScript 2015, section 12.5.8.1声明操作数在移位0位之前映射到无符号32位。所以,即使你将零位移,你也会看到这种效果。

您只需将其应用一次到最终值,例如在print函数中:

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

BigInt solution

如果你需要甚至超过32位,并且你的JavaScript引擎支持像BigInt那样的some,那么使用BigInts作为位运算符中涉及的操作数 - 这些将不会使用32位有符号数字包装(请注意n后缀):

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 ...

© www.soinside.com 2019 - 2024. All rights reserved.