2016-01-04 7 views
11

MIN_SAFE_INTEGERからMAX_SAFE_INTEGERまでのJavaScript番号(符号を含まない53ビット)の範囲を、符号とヌル識別子を考慮して2バイトシフトした7バイトにまたがるビット列に変換したいと考えています。JavaScriptで任意の順序のバイト配列に整数を変換する最速の方法は?

これまで私が思い付くした最高のは、次のとおりです。

function toUint8Array(data) { 
    data = data.toString(2); 
    data = new Array(65 - data.length).join('0') + data; 
    var ret = new Uint8Array(data.length/8); 
    for (var i = 0; i < 8; i++) { 
     ret[i] = 0; 
     ret[i] += (data[i * 8] == '1' ? 128 : 0); 
     ret[i] += (data[(i * 8) + 1] == '1' ? 64 : 0); 
     ret[i] += (data[(i * 8) + 2] == '1' ? 32 : 0); 
     ret[i] += (data[(i * 8) + 3] == '1' ? 16 : 0); 
     ret[i] += (data[(i * 8) + 4] == '1' ? 8 : 0); 
     ret[i] += (data[(i * 8) + 5] == '1' ? 4 : 0); 
     ret[i] += (data[(i * 8) + 6] == '1' ? 2 : 0); 
     ret[i] += (data[(i * 8) + 7] == '1' ? 1 : 0); 
    } 
    return (ret); 
} 

Fiddle

あなたは右のオフ伝えることができるように、これはひどく遅いだろう(とビットはまだシフトされていません7つのアクティブバイト全体に2つの場所があります)。

これを行う方法はありますか?理想的には、文字列解析を完全に避けることによって?

+0

実際のDataView、**正しく使用**つまりませんどのようにあなたがそれをしようとしてきたが、ささやかな(ChromeでFirefoxの、1.5倍で3倍、7.5倍** ** Internet Explorerでの)速度向上を与えることができます - 私はそれをやや最適にしているかもしれません –

+0

@JaromandaX私が得ようとしている出力をどのように管理しているのか不思議です。 – CoryG

+0

私はフィドルを作ることができますが、入力は厳密にMIN_SAFE_INTEGER - > MAX_SAFE_INTEGER - 1つの質問に限定されています...符号/ヌルビットは7バイト目のLSBか最初のバイトのMSBですか? –

答えて

1

私は本を叩きました。私は数人の数学者のCS友人と私たちの現在の評決は、あなたがそれを説明している間にこれを行うことはできないということです。

私はあなたが文字列解析に固執していると思います。

5

javascriptのビットワイズは32ビット幅です。しかし、シフトは2のべき乗による乗算または除算に相当し、これらは完全な浮動小数点精度で行われます。

だから、あなたがしたいことは簡単です。 Shiftキーを押して、下位ビットの興味のある部分を取得し、残りの部分をマスクします。 など。大きい番号0x123456789abc(20015998343868)があります。

0x123456789abc/0x1 = 0x123456789abc。 0xffのビット単位のANDは0xbcを与えます。

0x123456789abc/0x100 = 0x123456789a.bc。 0xffのビット単位のANDは0x9aを与えます。

0x123456789abc/0x10000 = 0x12345678.9abc。 0xffのビット単位のANDは0x78を与えます。

など。 Uint8Arraysは、0〜255の間の唯一の店の整数しかし、私は明確にするためにそれを残してきたことができますように0xffとマスキングは暗黙的である、となるように、結果は以下となります。コード:

function toUint8Array(d) { 
    var arr = new Uint8Array(7); 
    for (var i=0, j=1; i<7; i++, j *= 0x100) { 
     arr[i] = (d/j) & 0xff; 
    } 
    return arr; 
} 

Uint8Arrayの生活では、さらに簡単です異なる配列タイプでも同じです。

このコードは、リトルエンディアン配列を生成します。 toUint8Array(0x123456789abc)は、 [0xbc,0x9a,0x78,0x56,0x34,0x12,0]を返します。 ビッグエンディアン、つまり逆のバイト順のバイトを使用する場合は、arr[i]arr[6-i]に置き換えます。

(あなたが逆の順序で各列のエントリで ビットをしたい場合は、これは少し複雑ですbitrevはこのようなものになります bitrev((d/j) & 0xff)、と (d/j) & 0xffを置き換えます。

function bitrev(byte) { 
    var table = [ 0b0000, 0b1000, 0b0100, 0b1100, 0b0010, 0b1010, 0b0110, 0b1110, 
       0b0001, 0b1001, 0b0101, 0b1101, 0b0011, 0b1011, 0b0111, 0b1111 ]; 
    return table[byte >> 4] + (table[byte & 0xf] << 4); 
} 

を最後に、これは正の整数でのみ機能します。しかし、あなたのシフト・ツー・バイ・アイ・アイデアは簡単に実装されます。 d*4は、2ビットだけ左にシフトされます。d < 0 ? -d : d(またはMath.abs(d))の絶対値はdです。したがって、arr = toUint8Array((d<0) ? 1-d*4 : d*4)は、最下位ビット(LSB)の符号ビットを使用してdビットを左に2ビットシフトして返します。

そして、あなたはisFinite()でない-番号を確認することができますが、isFinite(null)として、数字だけでそれを呼び出すように注意する必要がありますが、たとえば、(これはES6で固定されている)による暗黙的キャストのルールに実際にtrueです:

function toUint8Array_shifted_signed(d) { 
    /* bit 0 is sign bit (0 for +ve); bit 1 is "not-a-number" */ 
    if (typeof d !== 'number' || !isFinite(d)) { 
     d = 2; 
    } else { 
     d = (d<0) ? 1-d*4 : d*4; 
    } 

    return toUint8Array(d); 
} 
+0

不思議です、それは速いですか? – Ross

+0

これは素晴らしいことですが、もう1つの質問ですが、元の53ビットのビットをすべて保存しながら2ビットシフトをすばやく行う方法はありますか? 'Number.MAX_SAFE_INTEGER/4'より大きい数値に対して' * 4'演算を行うと、間違ってしまうことがあります。 – CoryG

+1

※4はMAX_SAFE_INTEGER以上の数値であっても実際には安全です。内部的には仮数は同じですが、指数はちょうど2だけ増分されます。 MAX_SAFE_INTEGERは、MAX_SAFE_INTEGERより大きい* no *整数を無損失で表現できないことを意味するのではなく、存在できない整数だけを表すことを意味します。 しかし、そのコードはすべての正の整数に対して正しいですが、 '1-d * 4'は大きな*負の*整数に対して精度の低下を招く可能性があります。また、d> = 2^56のときオーバーフローのチェックもなく、整数でないdに対してガードがない(d * 4は小数部を下位2ビットにリークする可能性がある)。 – hexwab

関連する問題