From: Dr J R Stockton on
In comp.lang.javascript message <yxMdqJVxyOPMFwXk(a)invalid.uk.co.demon.me
rlyn.invalid>, Tue, 13 Jul 2010 23:46:09, Dr J R Stockton
<reply1028(a)merlyn.demon.co.uk> posted:

>Recent threads about errors in implementations of Number.toString(Radix)
>and the lack of an ECMA parseFloat(String, Radix) are now probably lost
>to most of you among the spam; hence this new one.

ISTM time to show this, from
<URL:http://www.merlyn.demon.co.uk/js-exact.htm>, where it is tested.
Essentially, it is parseFloat, but with a second argument for the radix,
and not handling floating-point strings (i.e. 123.456, but not
1.23456e2) (for those, see above on that page) (for algorithm, see
page).

Quicker methods have been presented, but they all seem to include
inexact operations. A single inexact operation may always give the best
available result; but if more than one are used, getting the best
available result probably cannot be guaranteed.

"Completely accurate" means that the Number given will always be the
IEEE Double nearest in value to the number represented by the input,
rounded up if there are two equal nearest.

Note that it can be meaningfully tested in Base 10, since that gets no
special treatment.



function refParseFixed(IN, Rdx) { var Sign = +1, J = 0, Tmp, Scale
// This is slow; it is intended to be completely accurate.
var Digits = "0123456789abcdefghijklmnopqrstuvwxyz"

Tmp = IN.charAt(0) // Handle possible sign
if (Tmp == "-") { J++ ; Sign = -1 } else if (Tmp == "+") J++

// Split IN into 2 arrays, Int & Frc, of 0 <= Number < Rdx
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 // incorrect digit ends
if (Tmp > 0) Cy = false // so not all zero
Num.push(Tmp) } // arrays now hold digit Numbers

if (Cy) return Sign * 0 // Zero (otherwise loops forever)

// Process integer part; repeatedly halve it to get binary bits :
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 leading 0s

J = Bin.length - 54 ; Scale = 0.5 ; while (--J >= 0) Scale /= 2

// Do fractional part; repeatedly double it to get binary bits :
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[0..52] into Num, scale it, add the Sign :
for (J = 0, Num = 0 ; J < 53 ; J++) { Num *= 2 ; Num += Bin[J] }
return Sign * Num / Scale } // end refParseFixed

--
(c) John Stockton, near London. *@merlyn.demon.co.uk/?.?.Stockton(a)physics.org
Web <URL:http://www.merlyn.demon.co.uk/> - FAQish topics, acronyms, & links.
Correct <= 4-line sig. separator as above, a line precisely "-- " (RFC5536/7)
Do not Mail News to me. Before a reply, quote with ">" or "> " (RFC5536/7)