Click here to Skip to main content
15,880,392 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I know that JavaScript can't precisely represent all 64 bit integer numbers. But it can precisely represent numbers larger than 32 bit. And that's what I need. With whatever precision JavaScript can give me.

I have a byte array of known length. It has 1, 2, 4, 8 or 16 bytes. And it can contain a signed or unsigned integer, I know which it is. The data is big-endian (network byte order).

How can I get the number value from that byte array?

There are simple solutions with multiply and add that completely fail on negative numbers. There's DataView that isn't of any help with more than 32 bits. I'm interested in a nice and simple and preferable efficient pure JavaScript solution to handle this. And unfortunately I can't think of such a way to handle negative two-complement values.

What I have tried:

I have tried it for positive values:

function readInt(array) {
    var value = 0;
    for (var i = 0; i < array.length; i++) {
        value = (value * 256) + array[i];
    }
    return value;
}


But it obviously fails on negative numbers, instead giving me unsigned values.
Posted
Updated 15-Oct-20 3:37am
Comments
Richard MacCutchan 20-Jun-18 4:48am    
If the array already contains a number (signed or not) what do you think your code is actually doing?
Yves Goergen 20-Jun-18 5:00am    
The code is decoding a big-endian unsigned integer in its current form. It does not decode a signed integer. For example -200 as int16 is encoded as FF 38, and this code then decodes it to 65336.
Richard MacCutchan 20-Jun-18 5:16am    
You should check the first byte to see if it is a negative value, and if so convert each byte to its opposite sign as you process them. You then need to reconvert the final value to negative as necessary.

When packing byte data into a wider integer, all bytes must be treated as unsigned and all operations must be performed unsigned.

This requires using bitwise operations:
javScript
for (var i = 0; i < array.length; i++) {
    value = (value << 8) | array[i];
}
But this is limited to 32-bit values because JavaScript ignores higher bits with bitwise operations.

A possible solution (I have not tested it) would be making the values unsigned:
javScript
for (var i = 0; i < array.length; i++) {
    value *= 256;
    if (array[i] < 0) {
        value += 256 + array[i];
    } else {
        value += array[i];
    }
}
The trick here is that the array values are bytes in the range -128 to +127. If a byte value is negative, 256 is added to make it the corresponding unsigned value.

It should work for up to 63 bits when the operations are performed using integers. If the JavaScript interpreter decides to perform floating point operations internally, the result will become inexact with more than 53 bits.

So there is no solution (as far as I know) for 128 bits (16 bytes).

[EDIT]
Because you need integer operations without rounding errors here, you can not expect proper results for final values greater than Number.MAX_SAFE_INTEGER - JavaScript | MDN[^] which is limited to 53 bits (the number of mantissa bits for double precision floating point values).
[/EDIT]
 
Share this answer
 
v2
Comments
Yves Goergen 20-Jun-18 5:41am    
Ehm, sorry, this is a Uint8Array, so no negative bytes here.
Yves Goergen 20-Jun-18 5:44am    
And stupid me, the array length can only be 1, 2, 4 or 8, not 16. Your code doesn't work for me. When I replace '< 0' with '>= 0x80', it produces even higher positive values, longer than possible with 16 bit.
Jochen Arndt 20-Jun-18 5:55am    
When the values are Uint8, there should be no need to check for values < 0 (I just acted according to "it can contain a signed or unsigned integer").

All I can suggest to use the first code snippet for 32 bit or less values.

For 64 bit values with the Interpreter will probabyl switch to floating point internally. However, you can try to use the 32 bit code twice for the upper and lower bits and combine them afterwards using multiplication and addition. But I guess it will be done again as floating point.
Yves Goergen 20-Jun-18 6:18am    
Not the bytes are negative. Bytes are never negative, they range from 0 to 255. The complete integer number is negative. And I have the bytes that represent that number.
Jochen Arndt 20-Jun-18 6:31am    
Yes, I forgot about that case.

The complete number is negative when the MSB of the first byte is set. The solution would be setting all high bits to one if that occurs. For the first (32 bit) code that could be done by setting the initial value to -1 (all bits set) when the MSB of the first byte is set.
This page explains a good and simple solution:

• Add up all bits from the right end, up to the second most significant bit.
• The most significant bit is not added as 2^i but as -2^i.

My code works in a larger scope that has an array and a pos to read from.

function readInt(size) {
	var value = 0;
	var first = true;
	while (size--) {
		if (first) {
			let byte = array[pos++];
			value += byte & 0x7f;
			if (byte & 0x80) {
				value -= 0x80;
				// Treat most-significant bit as -2^i instead of 2^i
			}
			first = false;
		}
		else {
			value *= 256;
			value += array[pos++];
		}
	}
	return value;
}


The raw bytes are provided in array (a Uint8Array) and pos is the next index to read. This function starts to read at the current pos and advances pos as it reads one of the size bytes.
 
Share this answer
 
Here is my solution to this problem:

// calculate 64-bit long in base 10
// bitwise operators stop working after 32 bits in JS 
// so we must use standard arithmetic operators:
var ret = 0;
if (bytes[7] >= 128) { // negative number
    bytes.forEach((val, i) => { ret += (255 - val) * 256 ** i });
    ret = ret * -1 - 1;
}
else bytes.forEach((val, i) => { ret += val * 256 ** i; });

Where "bytes" is a Uint8Array of size 8 (serialized 64-bit long). Keep in mind that the JS number type does not have the same range as typical 64-bit numbers like long/unsigned long. At the highest/lowest ranges you may see rounding issues or JS resolve this to "Infinity".
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900