Как создать 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