1. ホーム
  2. javascript

JavaScriptで数値を基数64に変換する最速の方法とは?

2023-11-10 06:47:09

質問

JavaScriptでは、数値を文字列に変換するために、特定の 基数 を使った文字列表現に変換することができます。

(12345).toString(36) // "9ix"

...そして、このように普通の数字に変換して戻すことができます。

parseInt("9ix", 36) // 12345

36は指定できる最も高い基数です。これはどうやら文字 0-9a-z を桁数分(合計36個)使用します。

私の質問:数値を基底64表現に変換する最も速い方法は何でしょうか(たとえば A-Z とか -_ で28桁になります)?


更新 : 4人の方が、この質問は重複している、あるいは私がBase64を探しているという回答を投稿されました。私はそうではありません。

"です。 ベース64 ネットワーク上での転送を安全にするために、バイナリ データを単純な ASCII 文字セットでエンコードする方法です (テキストのみのシステムではバイナリが文字化けしないようにします)。

私が質問しているのはそのことではありません。私が質問しているのは 数字 を基数64の文字列表現に変換することを尋ねているのです。(JavaScriptの toString(radix) は36までの任意の基数に対して自動的にこれを行います。私は基数64を得るためにカスタム関数が必要です)。


アップデート 2 : 以下は入力と出力の例です...。

0   → "0"
1   → "1"
9   → "9"
10  → "a"
35  → "z"
61  → "Z"
62  → "-"
63  → "_"
64  → "10"
65  → "11"
128 → "20"
etc.

どのように解決するのですか?

以下は、NUMBERS(バイト配列ではありません)に対する解決策のスケッチです。

正数のみ、小数部分を無視し、実際にテストしていません -- ただのスケッチです!

Base64 = {

    _Rixits :
//   0       8       16      24      32      40      48      56     63
//   v       v       v       v       v       v       v       v      v
    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/",
    // You have the freedom, here, to choose the glyphs you want for 
    // representing your base-64 numbers. The ASCII encoding guys usually
    // choose a set of glyphs beginning with ABCD..., but, looking at
    // your update #2, I deduce that you want glyphs beginning with 
    // 0123..., which is a fine choice and aligns the first ten numbers
    // in base 64 with the first ten numbers in decimal.

    // This cannot handle negative numbers and only works on the 
    //     integer part, discarding the fractional part.
    // Doing better means deciding on whether you're just representing
    // the subset of javascript numbers of twos-complement 32-bit integers 
    // or going with base-64 representations for the bit pattern of the
    // underlying IEEE floating-point number, or representing the mantissae
    // and exponents separately, or some other possibility. For now, bail
    fromNumber : function(number) {
        if (isNaN(Number(number)) || number === null ||
            number === Number.POSITIVE_INFINITY)
            throw "The input is not valid";
        if (number < 0)
            throw "Can't represent negative numbers now";

        var rixit; // like 'digit', only in some non-decimal radix 
        var residual = Math.floor(number);
        var result = '';
        while (true) {
            rixit = residual % 64
            // console.log("rixit : " + rixit);
            // console.log("result before : " + result);
            result = this._Rixits.charAt(rixit) + result;
            // console.log("result after : " + result);
            // console.log("residual before : " + residual);
            residual = Math.floor(residual / 64);
            // console.log("residual after : " + residual);

            if (residual == 0)
                break;
            }
        return result;
    },

    toNumber : function(rixits) {
        var result = 0;
        // console.log("rixits : " + rixits);
        // console.log("rixits.split('') : " + rixits.split(''));
        rixits = rixits.split('');
        for (var e = 0; e < rixits.length; e++) {
            // console.log("_Rixits.indexOf(" + rixits[e] + ") : " + 
                // this._Rixits.indexOf(rixits[e]));
            // console.log("result before : " + result);
            result = (result * 64) + this._Rixits.indexOf(rixits[e]);
            // console.log("result after : " + result);
        }
        return result;
    }
}

UPDATE: ここでは、console.logがあるNodeJsで実行するための、上記のいくつかの(非常に軽量な)テストを紹介します。

function testBase64(x) {
    console.log("My number is " + x);
    var g = Base64.fromNumber(x);
    console.log("My base-64 representation is " + g);
    var h = Base64.toNumber(g);
    console.log("Returning from base-64, I get " + h);
    if (h !== Math.floor(x))
        throw "TEST FAILED";
}

testBase64(0);
try {
    testBase64(-1);
    }
catch (err) {
    console.log("caught >>>>>>  " + err);
    }
try {
    testBase64(undefined);
    }
catch (err) {
    console.log("caught >>>>>>  " + err);
    }
try {
    testBase64(null);
    }
catch (err) {
    console.log("caught >>>>>>  " + err);
    }
try {
    testBase64(Number.NaN);
    }
catch (err) {
    console.log("caught >>>>>>  " + err);
    }
try {
    testBase64(Number.POSITIVE_INFINITY);
    }
catch (err) {
    console.log("caught >>>>>>  " + err);
    }
try {
    testBase64(Number.NEGATIVE_INFINITY);
    }
catch (err) {
    console.log("caught >>>>>>  " + err);
    }

for(i=0; i<100; i++)
    testBase64(Math.random()*1e14);