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;
}
I am trying to convert a javascript number to a byte array and then back to a number. However the above functions produce incorrect output with a very large number.
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
It is my understanding that javascript floats are 53
bits so it shouldn't be overflowing? alert(Math.pow(2,53))
works fine.
-
6Bit shifts are always performed on signed, 32-bits integers.Crozin– Crozin2011年12月12日 23:18:08 +00:00Commented Dec 12, 2011 at 23:18
-
That makes sense, make it an answer and i'll accept. Thanksuser75832– user758322011年12月12日 23:22:26 +00:00Commented Dec 12, 2011 at 23:22
-
4Since this question ranks high in google, it's probably worth pointing out that there are now array buffers: stackoverflow.com/questions/15761790/…Melvin Sovereign– Melvin Sovereign2020年02月07日 15:56:03 +00:00Commented Feb 7, 2020 at 15:56
11 Answers 11
Using the hint provided by Susanoh13, here are the two functions that allow conversion of number from/to 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;
};
5 Comments
[1, 128]
=== 32769
) If your input bytes are from most to least instead, you can increment from 0 in the loop instead of decrementing to 0.In JavaScript bit shifts (>>
, <<
) are always performed on signed, 32-bits integers. This leads to range overflow for large numbers.
6 Comments
BigInt
, but this isn't what I'd call a 64-bit numbertry (**
is power operator, <<
and >>>
are bit-shift operators) - intFromBytes
works only for arrays generated from positive integers
function getInt64Bytes(x) {
let y= Math.floor(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= Math.floor(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}`);
2 Comments
y
in getInt64Bytes
needs to be wrapped in Math.floor
or else it breaks for negative integers. For example, getInt64Bytes(-Number.MAX_SAFE_INTEGER)
returns [255, 224, 0, 1, 0, 0, 0, 1]
when it should return [255, 224, 0, 0, 0, 0, 0, 1]
.In 2022, you should use Buffer to deal with bytes, and BigInt to deal with big integers.
So your code may look like this:
import { Buffer } from "node:buffer";
/**
* @param {BigInt} x
* @returns {Buffer}
*/
function getInt64Bytes(x) {
const bytes = Buffer.alloc(8);
bytes.writeBigInt64LE(x);
return bytes;
}
/**
* @param {Buffer} x
* @returns {BigInt}
*/
function intFromBytes(x) {
return x.readBigInt64LE();
}
var array = getInt64Bytes(BigInt(23423423));
var value = intFromBytes(array);
console.log(value); // 23423423n
var array = getInt64Bytes(BigInt(45035996273704));
var value = intFromBytes(array);
console.log(value); // 45035996273704n
2 Comments
Brainfuck-style Lodash version. Just 4 lulz! don't use it!
const uintToArray = (uint, size) => _.chunk(_.padStart(uint, size*2, 0).split(''), 2).map((a)=>parseInt(a[0]+a[1]))
Comments
Doing a bit shift is the same as multiplying by 2^(# of bits+1), so instead of shifting the bits val = val<<8
, you can just do val = val*256
. See if that works.
Comments
<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>
2 Comments
If you happen to be on Node.js, Buffer
is the correct way to handle any byte array/stream in Javascript/Typescript:
https://nodejs.org/api/buffer.html
Although the docs are more comprehensive, Stack Overflow recommends code snippets here in case that link 404s, so here are a couple of the most important code examples in that doc:
// Creates a Buffer containing the UTF-8-encoded bytes for the string 'tést':
// [0x74, 0xc3, 0xa9, 0x73, 0x74] (in hexadecimal notation)
// [116, 195, 169, 115, 116] (in decimal notation)
const buf6 = Buffer.from('tést');
// Creates a Buffer containing the bytes [1, 2, 3].
const buf4 = Buffer.from([1, 2, 3]);
1 Comment
Just so that someone coming here after node version 16.5 knows that the buffer API from node.js now provides a way to write/read various sizes of numbers to byte array and vice-versa using the
writeUIntXXX
apis. These cover integers, long, double, float, and in Big and Small Endian formats. So I guess we don't need to spin off our own solutions anymore.
Comments
Modern solution for modern browsers
// Number or BigInt to Uint8Array
NumberToUint8Array = number => {
var array = [], bigint = BigInt(number)
for (let i = 0; i < Math.ceil(Math.floor(Math.log2(new Number(number)) + 1) / 8); i++)
array.unshift(new Number((bigint >> BigInt(8 * i)) & 255n))
return new Uint8Array(array)
}
// Uint8Array to BigInt
Uint8ArrayToBigInt = Uint8Array => [...Uint8Array].reverse().reduce((prev, curr, index) => BigInt(prev) | (BigInt(curr) << BigInt(index * 8)))
// example
NumberToUint8Array(12312321312312309876543234567890n)
// returns: [155, 103, 65, 124, 45, 109, 221, 194, 90, 249, 152, 238, 210]
Uint8ArrayToBigInt([155, 103, 65, 124, 45, 109, 221, 194, 90, 249, 152, 238, 210])
// returns: 12312321312312309876543234567890n
Comments
This will work,
let buf;
if (num < 128)
{
buf = Buffer.from([num]);
}
else if (num < 256)
{
buf = Buffer.from([129, num]);
}
else if (num < 65536)
{
buf = Buffer.from([130, 256, num % 256]);
}
else if (num < 65536)
{
buf = Buffer.from([130, num / 256, num % 256]);
}
else if (num < 16777216)
{
buf = Buffer.from([131, num / 65536, num / 256, num % 256]);
}
console.log(buf.toString('hex'));
1 Comment
num<65536
which I believe is a typo.