In JavaScript you can convert a number to a string representation with a specific radix as follows:
(12345).toString(36) // "9ix"
...and you can convert it back to a regular number like this:
parseInt("9ix", 36) // 12345
36 is the highest radix you can specify. It apparently uses the characters 0-9
and a-z
for the digits (36 total).
My question: what's the fastest way to convert a number to a base 64 representation (for example, using A-Z
, and -
and _
for the extra 28 digits)?
Update: Four people have posted responses saying this question is duplicated, or that I'm looking for Base64. I'm not.
"Base64" is a way of encoding binary data in a simple ASCII character set, to make it safe for transfer over networks etc. (so that text-only systems won't garble the binary).
That's not what I'm asking about. I'm asking about converting numbers to a radix 64 string representation. (JavaScript's toString(radix)
does this automatically for any radix up to 36; I need a custom function to get radix 64.)
Update 2: Here are some input & output examples...
0 → "0" 1 → "1" 9 → "9" 10 → "a" 35 → "z" 61 → "Z" 62 → "-" 63 → "_" 64 → "10" 65 → "11" 128 → "20" etc.
Fastest based on the JSPerf test above: str = num. toString();
Radix 64 or base 64 is a binary-to-text encoding system that is designed to allow binary data to be represented in ASCII string format. This is an educational project accepts a string from a user and Radix 64 encodes it. A GitHub repo accompanies this post, with a working example in C.
To convert a string into a Base64 character the following steps should be followed: Get the ASCII value of each character in the string. Compute the 8-bit binary equivalent of the ASCII values. Convert the 8-bit characters chunk into chunks of 6 bits by re-grouping the digits.
Here is a sketch for a solution for NUMBERS (not arrays of bytes :)
only for positive numbers, ignores fractional parts, and not really tested -- just a sketch!
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: Here's some (very lightweight) testing of the above, for running in NodeJs where you have console.log.
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);
Here's a version just for 32 bit ints, that is, any number between -2147483648 and 2147483647 (inclusive).
I modified the version in the top answer by Reb Cabin. This should be quite a bit faster because it uses bit operations and lookup tables.
Base64 = (function () { var digitsStr = // 0 8 16 24 32 40 48 56 63 // v v v v v v v v v "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-"; var digits = digitsStr.split(''); var digitsMap = {}; for (var i = 0; i < digits.length; i++) { digitsMap[digits[i]] = i; } return { fromInt: function(int32) { var result = ''; while (true) { result = digits[int32 & 0x3f] + result; int32 >>>= 6; if (int32 === 0) break; } return result; }, toInt: function(digitsStr) { var result = 0; var digits = digitsStr.split(''); for (var i = 0; i < digits.length; i++) { result = (result << 6) + digitsMap[digits[i]]; } return result; } }; })();
For example,
Base64.fromInt(-2147483648); // gives "200000" Base64.toInt("200000"); // gives -2147483648
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With