6

64ビット整数を表現できる他の言語では、これを非常に簡単に行うことができます...

64 ビット整数を 2 つの 32 ビット整数に格納し、再度変換する方法

Rubyで64ビット整数を2つの32ビット整数に格納する方法

// convert 64-bit n to two 32-bit x and y
x = (n & 0xFFFFFFFF00000000) >> 32
y =  n & 0xFFFFFFFF

しかし、JavaScript は 64 ビット整数を表現できません。問題なく52 ビット整数のみを表すことができます。

これは、64 ビット整数を 2 つの 32 ビット整数に変換することができないことを意味します。そもそも64 ビット整数を持つことさえできないからです。

しかし、まだ 52 ビット残っています。私の質問は、JavaScript でこの 52 ビット整数を 2 つの 32 ビット整数 (上位 20 ビットと下位 32 ビット) に分割するにはどうすればよいかということです。

JavaScript で 20 ビットと 32 ビットの分割を行うために、上記のようなビット操作コードを誰かが提案できますか?

関連: ビット単位の演算から得られた32ビットのJavaScript数値は、どのように64ビット数値に変換されるのですか

4

3 に答える 3

14

始める前に

まず第一に、あなたのリンクには、「2 52 [...]未満の整数は JavaScript の数値に安全に収まる」という軽微な誤りが含まれています。技術的には正しいですが、厳密な境界ではありません: 検証できますJavaScript の数値が 2 53までのすべての正の整数を格納できるという問題はあまりありません (ただし、2 53 +1 は格納できません)。

いくつかのコード

これ以上苦労することなく、52ビットの数値を下位32ビットと上位20ビットに分割する、あなたが要求した関数:

function to_int52(hi, lo) {
    /* range checking */
    if ((lo !== lo|0) && (lo !== (lo|0)+4294967296))
        throw new Error ("lo out of range: "+lo);
    if (hi !== hi|0 && hi >= 1048576)
        throw new Error ("hi out of range: "+hi);

    if (lo < 0)
    lo += 4294967296;

    return hi * 4294967296 + lo;
}

function from_int52(i) {
    var lo = i | 0;
    if (lo < 0)
    lo += 4294967296;

    var hi = i - lo;
    hi /= 4294967296;
    if ((hi < 0) || (hi >= 1048576)
        throw new Error ("not an int52: "+i);
    return { lo: lo, hi: hi };
}

分割する場所

ただし、これらを使用することはお勧めしません。Javascript のビット演算は符号付き (@dandavis: JS には UInt32 がありませ) であり、実際に正の値が必要な場合、符号ビットが頭痛の種になります。さらに、V8 には、31 ビットで格納できる (符号付き) 整数の最適化があります。これら 2 つの事実を組み合わせると、V8 短整数 ("smi") に収まる正の最大サイズである 30 ビット以下で分割する必要があります。

数値を 30 の下位ビットと 22 の上位ビットに分割するコードを次に示します。

function int52_30_get(i) {
    var lo = i & 0x3fffffff;
    var hi = (i - lo) / 0x40000000;
    return { lo: lo, hi: hi };
}

ただし、おそらくオブジェクトを作成したくないでしょう。これらはインライン化する必要があります(実際に関数を気にしている場合):

function int52_30_get_lo(i) {
    return i & 0x3fffffff;
}

function int52_30_get_hi(i) {
    return (i - (i & 0x3fffffff)) / 0x40000000;
}

そして、低い部分と高い部分から数値を作成するには:

function int52_30_new_safe(hi, lo) {
    return (hi & 0x3fffff) * 0x40000000 + (lo & 0x3fffffff);
}

hi と lo が範囲内にあると確信している場合は、マスキングをスキップできます。

function int52_30_new(hi, lo) {
    return hi * 0x40000000 + lo;
}

高音部と低音部を個別に設定します。

/* set high part of i to hi */
i = (hi & 0x3fffff) * 0x40000000 + (i & 0x3fffffff);

/* set low part of i to lo */
i += (lo & 0x3fffffff) - (i & 0x3fffffff);

hi と lo が範囲内にあることが確実な場合:

/* set high part of i to hi */
i = hi * 0x40000000 + (i & 0x3fffffff);

/* set low part of i to lo */
i += lo - (i & 0x3fffffff);

(これらは を変更するため、関数ではありませんi。)

さらにお楽しみに、任意のビットフィールドを引き出す関数:

function int52_30_get_bits(i, lsb, nbits) {
    while (lsb >= 32) {
        i /= 4294967296;
        lsb -= 32;
    }
    return (i / (1<<lsb)) & ((1<<nbits)-1);
}

(nbits は <= 31 である必要があります。nbits が 32 の場合の失敗モードは興味深いものであり、これは << の rhs オペランドの下位 5 ビットのみが重要であり、javascript 仕様が x86 ISA と共有する欠陥です。)

52ビット以上?

符号ビットを使用して、53 ビットの 2 進数を -2 53から 2 53 -1 の整数として格納することは完全に可能です。私はこれを行っていませんが、十分に簡単なはずです。その後、少し毛むくじゃらになり始め、最終的に 2 64に到達する前に、回るのに十分な float がない (多くは NaN である) という事実に遭遇します。63 の 2 進数を float にパックすることは、理論的には実行可能なはずですが、読者の演習として残されています :)

その他のアプローチ

もう 1 つの方法は、型付き配列を使用して Float ビューと Int ビューを作成することです。これにより、基になる float のバイナリ表現を直接操作できます。しかし、その後、エンディアンなどについて心配し始める必要があります。

文字列操作を提案しているすべての人は、ただの狂人です。

于 2013-10-09T14:15:36.410 に答える