Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fastest way to convert a number to radix 64 in JavaScript?

Tags:

javascript

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. 
like image 458
callum Avatar asked Jun 02 '11 10:06

callum


People also ask

What is the fastest way to convert number to string in JavaScript?

Fastest based on the JSPerf test above: str = num. toString();

What is Radix 64 format?

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.

How do you convert a string to Base64?

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.


2 Answers

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); 
like image 56
Reb.Cabin Avatar answered Sep 30 '22 17:09

Reb.Cabin


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 
like image 43
jahooma Avatar answered Sep 30 '22 16:09

jahooma