Как создать 32-разрядное целое число из восьми (8) 4-разрядных чисел? - PullRequest
13 голосов
/ 28 марта 2019

Допустим, у меня есть максимальное 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)

Пока все хорошо. Но теперь допустим, что я хочу создать 32-разрядное число, используя восемь (8) 4-разрядных чисел. Идея проста: сдвинуть (<<) каждую 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

Тем не менее, я получаю ту же ошибку с моей функцией make при попытке объединить все восемь (8) чисел -

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 :(

Что дает?

Цель состоит в том, чтобы преобразовать восемь (8) 4-разрядных целых чисел в одно 32-разрядное целое число с помощью JavaScript - это только моя попытка. Мне любопытно, где моя функция нарушается, но я открыт для альтернативных решений.

Я бы хотел избежать преобразования каждого 4-битного целого числа в двоичную строку, разбивать двоичные строки вместе, а затем анализировать двоичную строку в один тип int. Числовое решение является предпочтительным.

1 Ответ

13 голосов
/ 28 марта 2019

Битовые операторы приведут к знаку 32-битному числу, означающему, что если бит в позиции 31 (считая от младшего значащего бита справа, который является битом 0) равен 1, число будет отрицательным.

Чтобы этого не происходило, используйте операторы, отличные от << или |, которые приводят к получению 32-разрядного числа со знаком. Например:

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

Форсирование без знака 32-бит

Операторы сдвига битов предназначены для принудительного перевода результата в 32-битный диапазон со знаком, по крайней мере, заявленный для mdn (на момент написания):

Операнды всех битовых операторов преобразуются в 32-разрядные целые числа со знаком

Это на самом деле не совсем верно. Оператор >>> является исключением из этого. EcmaScript 2015, раздел 12.5.8.1 утверждает, что операнды отображаются в без знака 32 бита перед сдвигом в 0 бит. Таким образом, даже если бы вы сместили ноль битов, вы бы увидели этот эффект.

Вам нужно будет применить его только один раз к конечному значению, как, например, в вашей функции print:

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

BigInt решение

Если вам нужно больше 32 бит, и ваш движок JavaScript поддерживает BigInt , как , некоторые уже делают, тогда используйте 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...
}

NB. Если вы получаете сообщение об ошибке, попробуйте снова с Chrome ...

...