admin管理员组文章数量:1336656
Given a 64-bit (8-byte) little-endian ArrayBuffer
of bytes, how can we read 64-bit integer values in JavaScript?
I experimented and came up with this, but is there a more elegant solution given that DataView
does not yet provide getUint64()
?
const bytes = new Uint8Array([ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff ]);
// [----- left -----] [---- right ----]
const view = new DataView(bytes.buffer);
// split 64-bit number into two 32-bit numbers
const left = view.getUint32(0, true); // 4294967295
const right = view.getUint32(4, true); // 4294967295
// bine the 2 32-bit numbers using max 32-bit val 0xffffffff
const bined = left + 2**32*right;
console.log('bined', bined);
// 18,446,744,073,709,552,000 is returned Javascript for "bined"
// 18,446,744,073,709,551,615 is max uint64 value
// some precision is lost since JS doesn't support 64-bit ints, but it's close enough
Given a 64-bit (8-byte) little-endian ArrayBuffer
of bytes, how can we read 64-bit integer values in JavaScript?
I experimented and came up with this, but is there a more elegant solution given that DataView
does not yet provide getUint64()
?
const bytes = new Uint8Array([ 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff ]);
// [----- left -----] [---- right ----]
const view = new DataView(bytes.buffer);
// split 64-bit number into two 32-bit numbers
const left = view.getUint32(0, true); // 4294967295
const right = view.getUint32(4, true); // 4294967295
// bine the 2 32-bit numbers using max 32-bit val 0xffffffff
const bined = left + 2**32*right;
console.log('bined', bined);
// 18,446,744,073,709,552,000 is returned Javascript for "bined"
// 18,446,744,073,709,551,615 is max uint64 value
// some precision is lost since JS doesn't support 64-bit ints, but it's close enough
Share
Improve this question
edited Nov 1, 2018 at 16:41
anthumchris
asked Nov 1, 2018 at 14:49
anthumchrisanthumchris
9,0992 gold badges34 silver badges57 bronze badges
8
- 1 JS doesn't have 64 bit integers only 32 bit ones and 64 bit floats. – Sebastian Speitel Commented Nov 1, 2018 at 14:51
- 1 Thanks, I'm aware and curious how to make the code more concise, perhaps with bitwise? – anthumchris Commented Nov 1, 2018 at 14:59
-
2
Wouldn't be
sum = 2**32*right + left
enough? – Sebastian Speitel Commented Nov 1, 2018 at 15:01 - If you need the precision you can use the new BigInt. Reference: developers.google./web/updates/2018/05/bigint – Sebastian Speitel Commented Nov 1, 2018 at 15:03
-
As
getUint32()
always gets a non-negative number, luckily the bogus part of the ternary will never kick in. Still, you could remove it pletely, or at least make it==
– tevemadar Commented Nov 1, 2018 at 15:10
2 Answers
Reset to default 4Based on the original experiment and Sebastian Speitel
's suggestion/fix, this function returns a 64-bit value until precision is lost after Number.MAX_SAFE_INTEGER
DataView.prototype.getUint64 = function(byteOffset, littleEndian) {
// split 64-bit number into two 32-bit parts
const left = this.getUint32(byteOffset, littleEndian);
const right = this.getUint32(byteOffset+4, littleEndian);
// bine the two 32-bit values
const bined = littleEndian? left + 2**32*right : 2**32*left + right;
if (!Number.isSafeInteger(bined))
console.warn(bined, 'exceeds MAX_SAFE_INTEGER. Precision may be lost');
return bined;
}
Tested below:
// [byteArray, littleEndian, expectedValue]
const testValues = [
// big-endian
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]), false, 255],
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff]), false, 65535],
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]), false, 4294967295],
[new Uint8Array([0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]), false, 4294967296],
[new Uint8Array([0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), false, 9007199254740991], // maximum precision
[new Uint8Array([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), false, 9007199254740992], // precision lost
[new Uint8Array([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), false, 9007199254740992], // precision lost
// little-endian
[new Uint8Array([0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), true, 255],
[new Uint8Array([0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), true, 65535],
[new Uint8Array([0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]), true, 4294967295],
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]), true, 4294967296],
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00]), true, 1099511627776],
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00]), true, 281474976710656],
[new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00]), true, 9007199254740991], // maximum precision
];
testValues.forEach(testGetUint64);
function testGetUint64([bytes, littleEndian, expectedValue]) {
const val = new DataView(bytes.buffer).getUint64(0, littleEndian);
console.log(val === expectedValue? 'pass' : 'FAIL. expected '+expectedValue+', received '+val);
}
Some browsers are starting to support the experimental BigInt
global object:
BigInt
is a built-in object that provides a way to represent whole numbers larger than 253, which is the largest number JavaScript can reliably represent with the Number primitive.
If you are only targeting these browsers then you can use this to get larger values than can be supported by a Number
. Additionally, Chrome currently supports the DataView.getBigInt64( position, littleEndian )
and DataView.getBigUint64( position, littleEndian )
functions that return BigInt
values.
Read 64-bit unsigned values
function getBigUint64( view, position, littleEndian = false )
{
if ( "getBigUint64" in DataView.prototype )
{
return view.getBigUint64( position, littleEndian );
}
else
{
const lsb = BigInt( view.getUint32( position + (littleEndian ? 0 : 4), littleEndian ) );
const gsb = BigInt( view.getUint32( position + (littleEndian ? 4 : 0), littleEndian ) );
return lsb + 4294967296n * gsb;
}
}
Read 64-bit signed values:
function getBigInt64( view, position, littleEndian = false )
{
if ( "getBigInt64" in DataView.prototype )
{
return view.getBigInt64( position, littleEndian );
}
else
{
let value = 0n;
let isNegative = ( view.getUint8( position + ( littleEndian ? 7 : 0 ) ) & 0x80 ) > 0;
let carrying = true;
for ( let i = 0; i < 8; i++ )
{
let byte = view.getUint8( position + ( littleEndian ? i : 7 - i ) );
if ( isNegative )
{
if ( carrying )
{
if ( byte != 0x00 )
{
byte = (~(byte - 1))&0xFF;
carrying = false;
}
}
else
{
byte = (~byte)&0xFF;
}
}
value += BigInt(byte) * 256n**BigInt(i);
}
if ( isNegative )
{
value = -value;
}
return value;
}
}
Tests:
function getBigInt64( view, position, littleEndian = false )
{
if ( "getBigInt64" in DataView.prototype )
{
return view.getBigInt64( position, littleEndian );
}
else
{
let value = 0n;
let isNegative = ( view.getUint8( position + ( littleEndian ? 7 : 0 ) ) & 0x80 ) > 0;
let carrying = true;
for ( let i = 0; i < 8; i++ )
{
let byte = view.getUint8( position + ( littleEndian ? i : 7 - i ) );
if ( isNegative )
{
if ( carrying )
{
if ( byte != 0x00 )
{
byte = (~(byte - 1))&0xFF;
carrying = false;
}
}
else
{
byte = (~byte)&0xFF;
}
}
value += BigInt(byte) * 256n**BigInt(i);
}
if ( isNegative )
{
value = -value;
}
return value;
}
}
// [byteArray, littleEndian, expectedValue]
const testValues = [
// big-endian
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]), false, 255n],
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff]), false, 65535n],
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]), false, 4294967295n],
[new Uint8Array([0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]), false, 4294967296n],
[new Uint8Array([0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), false, 9007199254740991n],
[new Uint8Array([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), false, 9007199254740992n],
[new Uint8Array([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), false, 9007199254740993n],
[new Uint8Array([0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), false, (2n**63n)-1n],
[new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), false, -1n],
[new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00]), false, -256n],
[new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF]), false, -257n],
[new Uint8Array([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), false, -(2n**63n)],
// little-endian
[new Uint8Array([0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), true, 255n],
[new Uint8Array([0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), true, 65535n],
[new Uint8Array([0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]), true, 4294967295n],
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]), true, 4294967296n],
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00]), true, 1099511627776n],
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00]), true, 281474976710656n],
[new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00]), true, 9007199254740991n],
[new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F]), true, (2n**63n)-1n],
[new Uint8Array([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), true, -1n],
[new Uint8Array([0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), true, -256n],
[new Uint8Array([0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), true, -257n],
[new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), true, -(2n**63n)]
];
testValues.forEach(
function( [bytes, littleEndian, expectedValue] ) {
const val = getBigInt64( new DataView(bytes.buffer), 0, littleEndian );
console.log(
val === expectedValue
? 'pass'
: `FAIL. expected ${expectedValue}, received ${val}` );
}
);
本文标签: How to Read 64bit Integer from an ArrayBufferDataView in JavaScriptStack Overflow
版权声明:本文标题:How to Read 64-bit Integer from an ArrayBufferDataView in JavaScript - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742337360a2455892.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论