A discussion in a comp.lang.javascript thread is attempting to find a reasonable way to convert non-integer strings to numbers using bases other than 10. The functions listed below are the major ones discussed so far. Enter a string representing a number and the base to parse it in and submit the form. The output from each of these algorithms is displayed in the results table. Note that it is converted back into the original format using value.toString(base).
This does not attempt to compare performance, and by inspection it's clear that there are likely to be significant differences in performance.
var parseFloat_tl = (function () {
var origPF = parseFloat;
return function (s, iBase) {
if (!iBase || iBase == 10)
{
return origPF(s);
}
var
i = (s.indexOf(".") != 0 ? parseInt(s, iBase) : 0),
chars = (iBase < 10
? "0-" + String.fromCharCode(47 + iBase)
: "\\d"
+ (iBase > 10
? "a"
+ (iBase > 11
? "-" + String.fromCharCode(86 + iBase)
: "")
: "")),
f = (s.match(new RegExp("\\.([" + chars + "]+)", "i")) || [, "0"])[1];
return i + (i < 0 ? -1 : 1) * parseInt(f, iBase) / Math.pow(iBase, f.length);
};
}());
String.prototype.toFP_old= function (base, n, r, w, div) {
if (/[^0-9a-z\.+-]/i.test(this)) return NaN;
n= this.split('.');
if (isFinite(r= parseInt(n[0], base))) {
if (w= (n= n[1]).length) {
/*trim until it's finite*/
while (!isFinite(div= Math.pow(base, w))) w--;
r+= (r<0 ? -1:1)* parseInt(n.substr(0, w), base)/ div;
}
}
return r;
};
String.prototype.toFP= (function (regExpCache) {
/* 20100531, by jo...@jorgechamorro.com */
return function (base, n, r, w, div) {
if ((base < 2) || (base > 36) || (base % 1)) return NaN;
if (!(n= regExpCache[base])) {
n= "0123456789abcdefghijklmnopqrstuvwxyz".substr(0, base);
n= "^\\s{0,}([-+]{0,1})(["+n+"]{0,})[.]{0,1}(["+n+"]{0,})\
\s{0,}$";
regExpCache[base]= n= new RegExp(n, "i");
}
if (!(n= n.exec(this))) return NaN;
if (isFinite(r= parseInt(n[2] || "0", base)) && (w= n[3].length)) {
while (!isFinite(div= Math.pow(base, w))) w--;
r+= parseInt(n[3].substr(0, w), base)/ div;
}
return (n[1]+ "1")* r;
};
})([]);
var parseFloat_ss = (function() {
var origPF = parseFloat,
allDigits = "0123456789abcdefghijklmnopqrstuvwxyz",
regexes = {},
getRegex = function(base) {
if (!regexes[base]) {
var digits = allDigits.substring(0, base);
regexes[base] = new RegExp("(^[\\-\\+]?)([" + digits +
"]*)(?:\.([" + digits + "]*))?$");
}
return regexes[base];
},
parseFraction = function(str, base) {
if (!str) return 0;
var digits = str.split(""), total = 0;
for (var i = digits.length; i--;) {
total += allDigits.indexOf(digits[i]);
total /= base;
}
return total;
};
return function (str, base) {
if (!base || base == 10) {
return origPF(str);
}
if ((base < 2) || (base > 36) || (base % 1)) return NaN;
str = str.toString().toLowerCase();
var regex = getRegex(base),
match = regex.exec(str);
if (!match) return NaN;
return ((match[1] == "-") ? -1 : 1) * (
(match[2] == "" ? 0 : parseInt(match[2], base)) + parseFraction(match[3], base)
);
};
}());
(website)
function parsFixdB(S, R) {
S = S.replace(/(\w*\.)/i, "0$1") // ensure digit before point
var A = parseInt(S, R)
if (S = S.split(".")[1]) { var NR = 1, L = 0
S = S.substring(0, 99) // Crude partial fix for excess length
while (1+parseInt(S.charAt(L++), R)) NR *= R // good digits
A += (1/A>0?+1:-1) * parseInt(S, R) / NR }
return A }
(website)
function pInt(Ch, Rdx) {
var T = pInt.Digits.indexOf(Ch.toLowerCase())
return (T < 0 || T >= Rdx) ? NaN : T }
pInt.Digits = "0123456789abcdefghijklmnopqrstuvwxyz"
function parsFixdD(S, Rdx) { // was BetterPF
var J, L = 0, R = 0, RN = 1, Sgn = 1
S = S.split(".")
var Int = S[0].split(""), Frc = S[1].split("")
if (Int[0] == "-") { Sgn = -1 ; Int.shift(1) }
if (Int[0] == "+") { Sgn = +1 ; Int.shift(1) }
for (J = 0 ; J < Int.length ; J++)
L = L * Rdx + pInt(Int[J], Rdx)
for (J = 0 ; J < Frc.length ; J++) { RN *= Rdx
R = R * Rdx + pInt(Frc[J], Rdx) }
return Sgn * ( L + R/RN ) } // Consider case of L or R over 2^53
String.prototype.toNumber=function(radix){
radix=(radix!==undefined)?(+radix):10;
if((radix < 1)||(radix>36))throw new RangeError("Bad radix");
var s=(""+this).toLowerCase(),whitespaces=" \t\r\n",signs="-+";
var n="0123456789abcdefghijklmnopqrstuvwxyz".substr(0,radix),intValue=0,
fracValue=0,signValue=1,fracFactor=1,i,j=s.length,c,v;
for(i=0;(i < j)&&(whitespaces.indexOf(c=s.charAt(i))>=0);i++);
for(;(i < j)&&(signs.indexOf(c=s.charAt(i))>=0);i++)signValue*=(c=="-")?(-1):1;
for(;(i < j)&&(whitespaces.indexOf(c=s.charAt(i))>=0);i++);
for(;(i < j)&&((v=n.indexOf(c=s.charAt(i)))>=0);intValue=(intValue*radix)+v,i++);
for(i+=(c==".")?1:(j+1);(i < j)&&((v=n.indexOf(c=s.charAt(i)))>=0);fracValue+=v*(fracFactor/=radix),i++);
return(((intValue+fracValue)*(((c=="e")||(c=="p"))?Math.pow(radix,parseInt(s.substr(++i))):1.0))*signValue);
}
var parseFloat_ss2 = (function() {
var origPF = parseFloat,
DEPTH = 2,
maxInt = Math.pow(2, 53),
allDigits = "0123456789abcdefghijklmnopqrstuvwxyz",
regexes = [null, null],
powers = [null, null],
parseFraction = function(str, base) {
if (!str) return 0;
var basePowers = powers[base],
max = basePowers[basePowers.length - 1];
total = 0,
denominator = 1,
count = 0;
while (str.length && count < DEPTH) {
var index = Math.min(str.length, basePowers.length - 1);
total += parseInt(str.substring(0, index), base) / (denominator * basePowers[index]);
str = str.substring(index);
denominator *= max;
count++;
}
return total;
};
(function() {
for (var base = 2; base <=36; base++) {
var digits = allDigits.substring(0, base);
regexes[base]= new RegExp("(^[\\-\\+]?)([" + digits +
"]*)(?:\\.([" + digits + "]*))?$", "i");
powers[base] = [];
var power = 1, i = 0;
while (power <= maxInt) {
powers[base][i] = power;
power *= base;
i++;
}
}
}());
return function (str, base) {
if (!base || base == 10) return origPF(str);
if ((base < 2) || (base > 36) || (base % 1)) return NaN;
var regex = regexes[base],
match = regex.exec(str);
if (!match) return NaN;
return ((match[1] == "-") ? -1 : 1) * (
(match[2] == "" ? 0 : parseInt(match[2], base)) + parseFraction(match[3], base)
);
};
}());
(website)
function refParseFixed(IN, Rdx) { var Tmp, Sgn = +1, J = 0, Scale
// May be slow; is intended to be completely accurate.
var Digits = "0123456789abcdefghijklmnopqrstuvwxyz"
Tmp = IN.charAt(0) // possible sign
if (Tmp == "-" || Tmp == "+") { J = 1
if (Tmp == "-") Sgn = -1 }
var Num = Int = [], Frc = [], K = IN.length, Cy = true, Bin = []
while (J < K) { Tmp = IN.charAt(J++) // read char from string
if (Tmp == "." && Num == Int) { Num = Frc ; continue }
Tmp = Digits.indexOf(Tmp.toLowerCase()) // char to Number
if (Tmp < 0 || Tmp >= Rdx) break
if (Tmp > 0) Cy = false // so not all zero
Num.push(Tmp) } // arrays now hold digit Numbers
if (Cy) return Sgn*0 // Zero (otherwise loops forever)
// Process integer part :
while (J = Int.length) { // not ==
for (K = 0, Cy = 0 ; K < J ; K++) { // halving loop
Tmp = Cy * Rdx + Int[K] ; Cy = Tmp % 2 ; Int[K] = (Tmp-Cy) / 2 }
Bin.push(Cy)
while (Int[0] == 0) Int.shift() }
Bin.reverse()
while (Bin.length && Bin[0]==0) Bin.shift() // Omit any ldg zeroes
J = Bin.length - 54 ; Scale = 0.5 ; while (--J >= 0) Scale /= 2
// Process fractional part :
while (Bin.length < 54) { Cy = 0 ; K = Frc.length
while (K--) { // doubling loop
Tmp = Frc[K] * 2 + Cy ; Cy = +(Tmp>=Rdx) ; Frc[K] = Tmp % Rdx }
if (Bin.length || Cy == 1) Bin.push(Cy)
Scale *= 2 }
Bin[52] += Bin[53] // Rounding: now use Bin[0..52]
// Evaluate Bin and scale it :
for (J = 0, Num = 0 ; J < 53 ; J++) { Num *= 2 ; Num += Bin[J] }
return Sgn * Num / Scale } // end refParseFixed