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

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

Ответ 1

Побитовые операторы приведут к знаку 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...
}

Ответ 2

Для тех, кто заинтересован, я хотел сделать игрушечную PRG, которая была бы посеяна публичным броском костей, предоставленным ботом Discourse. У бота есть ограничение, что он может бросать только до 20 кубиков, каждый из которых имеет до 120 сторон. Так что бросить одиночную 4294967296-гранную (32-битную) матрицу не могло быть и речи. Бросать четыре 256-гранных (8-битных) кубиков тоже было невозможно, поэтому я остановился на восьми 16-гранных (4-битных) кубиках. Все работает сейчас благодаря @trincot. Я подумал, что поделюсь этим, чтобы вы могли видеть, что проблема немного уменьшена.

// simple prg c/o (https://stackoverflow.com/a/47593316/633183)
function mulberry32(a) {
    return function() {
      var t = a += 0x6D2B79F5;
      t = Math.imul(t ^ t >>> 15, t | 1);
      t ^= t + Math.imul(t ^ t >>> 7, t | 61);
      return ((t ^ t >>> 14) >>> 0) / 4294967296;
    }
}

// fixed function, thanks to @trincot
const roll2seed = ([ roll, ...more ], e = 0) =>
  roll === undefined
    ? 0
    : ((roll - 1) * 2**e) + roll2seed (more, e + 4) // subtract 1 from each roll, for 0-15

// roll from the discourse bot
const roll =
  [ 3, 6, 7, 5, 13, 16, 4, 6 ] // values from 1-16

// create seed
const seed =
  roll2seed (roll)

// our seeded random number generator, replaces Math.random()
const prng =
  mulberry32(seed)

// re-run multiple times to verify the output doesn't change
console.log(prng(), prng(), prng(), prng(), prng())

// 0.12690125219523907 0.28824569564312696 0.13434493844397366 0.4453472560271621 0.23281447542831302