繁体   English   中英

如何从JavaScript中的ArrayBuffer / DataView读取64位整数

[英]How to Read 64-bit Integer from an ArrayBuffer / DataView in JavaScript

给定一个64位(8字节)的little-endian ArrayBuffer字节,我们如何在JavaScript中读取64位整数值?

我进行了实验并想出了这个,但是有一个更优雅的解决方案,因为DataView还没有提供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

// combine the 2 32-bit numbers using max 32-bit val 0xffffffff
const combined = left + 2**32*right;

console.log('combined', combined);
// 18,446,744,073,709,552,000  is returned Javascript for "combined"
// 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

基于原始实验和Sebastian Speitel的建议/修复,此函数返回64位值,直到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);

  // combine the two 32-bit values
  const combined = littleEndian? left + 2**32*right : 2**32*left + right;

  if (!Number.isSafeInteger(combined))
    console.warn(combined, 'exceeds MAX_SAFE_INTEGER. Precision may be lost');

  return combined;
}


测试如下:

// [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);
}

一些浏览器开始支持实验性BigInt全局对象:

BigInt是一个内置对象,它提供了一种表示大于2 53的整数的方法,这是JavaScript可以用Number原语可靠表示的最大数字。

如果您只是针对这些浏览器,那么您可以使用它来获得比Number支持的值更大的值。 此外,Chrome目前支持返回BigInt值的DataView.getBigInt64( position, littleEndian )DataView.getBigUint64( position, littleEndian )函数。

读取64位无符号值

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;
  }
}

读取64位有符号值

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;
  }
}

测试

 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}` ); } ); 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM