Ответ 1
В сдвиге бит JavaScript (>>
, <<
) всегда выполняются для подписанных 32-битных целых чисел. Это приводит к переполнению диапазона для больших чисел.
function intFromBytes( x ){
var val = 0;
for (var i = 0; i < x.length; ++i) {
val += x[i];
if (i < x.length-1) {
val = val << 8;
}
}
return val;
}
function getInt64Bytes( x ){
var bytes = [];
var i = 8;
do {
bytes[--i] = x & (255);
x = x>>8;
} while ( i )
return bytes;
}
Я пытаюсь преобразовать число javascript в массив байтов, а затем обратно в число. Однако вышеприведенные функции дают неправильный вывод с очень большим числом.
var array = getInt64Bytes(23423423);
var value = intFromBytes(array);
console.log(value); //Prints 23423423 - correct
var array = getInt64Bytes(45035996273704);
var value = intFromBytes(array);
console.log(value); //Prints -1030792152 - incorrect
Насколько я понимаю, плавающие файлы JavaScript составляют 53
бита, поэтому они не должны быть переполнены? alert(Math.pow(2,53))
работает нормально.
В сдвиге бит JavaScript (>>
, <<
) всегда выполняются для подписанных 32-битных целых чисел. Это приводит к переполнению диапазона для больших чисел.
Используя подсказку, предоставленную Susanoh13, вот две функции, которые позволяют преобразовать число из/в ByteArray:
longToByteArray = function(/*long*/long) {
// we want to represent the input as a 8-bytes array
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0];
for ( var index = 0; index < byteArray.length; index ++ ) {
var byte = long & 0xff;
byteArray [ index ] = byte;
long = (long - byte) / 256 ;
}
return byteArray;
};
byteArrayToLong = function(/*byte[]*/byteArray) {
var value = 0;
for ( var i = byteArray.length - 1; i >= 0; i--) {
value = (value * 256) + byteArray[i];
}
return value;
};
Выполнение бит-сдвига такое же, как умножение на 2 ^ (# бит + 1), поэтому вместо сдвига битов val = val<<8
вы можете просто сделать val = val*256
. Посмотрите, работает ли это.
Версия Lodash в стиле Brainfuck. Всего 4 лулза! не используйте его!
const uintToArray = (uint, size) => _.chunk(_.padStart(uint, size*2, 0).split(''), 2).map((a)=>parseInt(a[0]+a[1]))
<html>
<head>
<meta charset="utf-8">
<title>Uint32_To_Byte_Array</title>
<script>
function body_Add(Msg)
{
document.body.innerHTML = document.body.innerHTML + Msg;
}
class Byte
{
constructor(Value)
{
this.Number = new Uint8Array(1);
this.Number[0] = Value;
}
get Get()
{
return this.Number[0];
}
set Set(newValue)
{
this.Number[0] = newValue;
}
};
class Uint32
{
constructor(Value)
{
this.Number = new Uint32Array(1);
this.Number[0] = Value;
}
get Get()
{
return this.Number[0];
}
set Set(newValue)
{
this.Number[0] = newValue;
}
};
var Conversion =
{
Uint32_To_Byte_Array: function(Source_Num)
{
var Uint32_Num = new Uint32(Source_Num);
var Byte_Num = new Byte(0);
var Byte_Arr = new Uint8Array(4);
for (var i = 0; i < 4; i++)
{
if (Source_Num > 255)
{
Uint32_Num.Set = Source_Num / 256;
Byte_Num.Set = Source_Num - Uint32_Num.Get * 256;
}
else
{
Byte_Num.Set = Uint32_Num.Get;
Uint32_Num.Set = 0;
}
Byte_Arr[i] = Byte_Num.Get;
Source_Num = Uint32_Num.Get;
}
return(Byte_Arr);
},
Byte_Array_To_Uint32: function(Source_Byte_Array, Start_Position)
{
var Uint32_Num = new Uint32(0);
var Multiplier = 1;
for (let i = 0; i < 4; i++)
{
Uint32_Num.Set = Uint32_Num.Get + Source_Byte_Array[Start_Position + i] * Multiplier;
Multiplier = Multiplier * 256;
}
return (Uint32_Num.Get);
}
};
function Load_Page()
{
var Numbers = [0,1,257,4294967295];
Numbers.forEach(Convert);
function Convert(Item, Index)
{
var Uint32_Number = Item;
var Byte_Array = Conversion.Uint32_To_Byte_Array(Uint32_Number);
var Uint32_Number_Restored = Conversion.Byte_Array_To_Uint32(Byte_Array, 0);
body_Add("Conversion: Source number: " + Uint32_Number.toString() + ", Byte array: " + Byte_Array.toString() + ", Restored number: " + Uint32_Number_Restored.toString() + "<br>");
};
};
</script>
</head>
<body onload="Load_Page()">
</body>
пытаться
function getInt64Bytes(x) {
let y= x/2**32;
return [y,(y<<8),(y<<16),(y<<24), x,(x<<8),(x<<16),(x<<24)].map(z=> z>>>24)
}
function intFromBytes(byteArr) {
return byteArr.reduce((a,c,i)=> a+c*2**(56-i*8),0)
}
function getInt64Bytes(x) {
let y= x/2**32;
return [y,(y<<8),(y<<16),(y<<24), x,(x<<8),(x<<16),(x<<24)].map(z=> z>>>24)
}
function intFromBytes(byteArr) {
return byteArr.reduce((a,c,i)=> a+c*2**(56-i*8),0)
}
// TEST
let n= 40*2**40 + 245*2**32 + 194*2**24 + 143*2**16 + 92*2**8 + 40;
let b= getInt64Bytes(n);
let i = intFromBytes(b);
console.log('number : ${n}');
console.log('int to bytes: [${b}]');
console.log('bytes to int: ${i}');