From: Dr J R Stockton on
In comp.lang.javascript message <cc0324be-9889-4ec7-b673-27c06cb50e83(a)b4
g2000pra.googlegroups.com>, Wed, 30 Jun 2010 08:12:20, RobG
<rgqld(a)iinet.net.au> posted:

>I'm working on a function to convert decimal integers to binary.


Since I don't want the page to be indexed, the following link is in ROT-
13; but the only un-guessable part is "base-cnv".

Temporary page <HEY:uggc://jjj.zreyla.qrzba.pb.hx/onfr-pai.ugz>
demonstrates conversion of a signed fixed-point numeric String from any
integer base greater than 1 to any other such base, and back. The
number of fractional digits in the result is just such that the LSB of
the LSD of the result is worth no more than the LSB of the LSD of the
input.

At present, the result is truncated rather than rounded, alas.

The method of converting between digit character and Number allows easy
conversion to any ordered set of digits, the default being as many as
are needed of 0-9a-z. So the greatest radix of the algorithm (in
JavaScript) is presumably 65536.

The length of the strings is unbounded (there is no internal conversion
of the whole input to a single Number) but the code assumes (at present)
that the LSB of the input is worth no less than the smallest non-zero
Number.

It is all very greatly under-tested.

--
(c) John Stockton, nr London, UK. ?@merlyn.demon.co.uk Turnpike v6.05.
Web <URL:http://www.merlyn.demon.co.uk/> - w. FAQish topics, links, acronyms
PAS EXE etc : <URL:http://www.merlyn.demon.co.uk/programs/> - see 00index.htm
Dates - miscdate.htm estrdate.htm js-dates.htm pas-time.htm critdate.htm etc.
From: RobG on
On Jul 6, 10:54 pm, Richard Cornford <Rich...(a)litotes.demon.co.uk>
wrote:
> On Jul 6, 3:29 am, Stefan Weiss wrote:
[...]
> <snip>> I couldn't find a way to try out the "substract 3" suggestion
> > without introducing at least one if-statement.
>
> <snip>
>
> What is wrong with the - if - statement?

Nothing, if it's faster than the alternative (and it is). Using the
shift operator is easily the fastest way to divide and round, that was
simple to implement. However, your suggestion of adding high bits and
subtracting 3 had me tossed for a while but then I realised it's the
same as right-shift then add 5. That didn't help (or hinder) IE but
did help Firefox.

The version below has what seem to be the best optimisations from
previous suggestions tested in IE 6 and Firefox. It is nearly the
fastest in IE and easily the fastest (so far) in Firefox. It uses a
combination of array and string methods, the fastest were selected
based on tests in the two browsers mentioned. I'll test more widely
later.

Optimisations worth mentioning are:

1. IE much prefers storing values to indexed lookups - even removing
one lookup had a big effect.

2. Replacing the if statement with a ternary operator is very slow in
IE, so more code is faster in this case.

3. Replacing the if statement to see if offset needed incrementing
with a type-conversion assignment made not much difference in IE but
really helped Firefox, i.e. the line:

offset += !bigInt[offset];

Hopefully conversion from Boolean to number is robust (though JRS's
comments about the vagaries of number.toString(radix) have me
worried).

var toBin = function(numStr) {
var bigInt = numStr.split(""),
len = bigInt.length,
result = "",
offset = 0,
rem, divPart, i, n;

do {
// Progressively halve number one digit at a time
for (rem=0, i=offset; i < len; ++i) {
n = bigInt[i];

// If previous digit was odd, halve this number
// and round down (trim rh bit) and add 5
if (rem) {
bigInt[i] = (n >>> 1) + 5;

// Otherwise, just halve (trim rh bit)
} else {
bigInt[i] = n >>> 1;
}

// Remember if this digit was odd for next loop
rem = n & 1;
}
// If digit is now zero, move to next
offset += !bigInt[offset];

// Prepend 1 to result if number was odd, otherwise 0
result = rem + result;

} while (offset < len);

return result;
};


--
Rob
From: Stefan Weiss on
On 07/07/10 06:06, RobG wrote:
> On Jul 6, 10:54 pm, Richard Cornford <Rich...(a)litotes.demon.co.uk>
> wrote:
>> On Jul 6, 3:29 am, Stefan Weiss wrote:
> [...]
>> <snip>> I couldn't find a way to try out the "substract 3" suggestion
>> > without introducing at least one if-statement.
>>
>> <snip>
>>
>> What is wrong with the - if - statement?
>
> Nothing, if it's faster than the alternative (and it is).

[...]

> However, your suggestion of adding high bits and
> subtracting 3 had me tossed for a while but then I realised it's the
> same as right-shift then add 5. That didn't help (or hinder) IE but
> did help Firefox.

That would appear to depend on the Firefox version. Avoiding the
if-statement actually improved performance in my stand-alone
SpiderMonkey versions (tested 1.7.0 and 1.8.0rc1). The JS engine in
current Firefox versions may give different results, possibly as a
result of JIT compilation. On the other hand, the trunk version of V8
also runs the code (marginally) faster when there is no "if" statement
in the "for" loop.

> The version below has what seem to be the best optimisations from
> previous suggestions tested in IE 6 and Firefox. It is nearly the
> fastest in IE and easily the fastest (so far) in Firefox. It uses a
> combination of array and string methods, the fastest were selected
> based on tests in the two browsers mentioned. I'll test more widely
> later.

The IE results are interesting, thank you. I didn't bother to run the
tests in a browser, but a general implementation should still run fast
enough in IE 6/7.

> 3. Replacing the if statement to see if offset needed incrementing
> with a type-conversion assignment made not much difference in IE but
> really helped Firefox, i.e. the line:
>
> offset += !bigInt[offset];

Interesting. I saw the opposite effect in V8 and SpiderMonkey.

> Hopefully conversion from Boolean to number is robust (though JRS's
> comments about the vagaries of number.toString(radix) have me
> worried).

I don't think those problems are related to the toBin() function.

> var toBin = function(numStr) {
[snip code]

I think we're well into the area of engine specific optimizations now.
The version I posted still runs fastest for me on the command line, but
your tests in IE and Firefox should make your version a better
compromise on the web.

One positive side effect of this exercise was to finally bring me to
update my outdated JavaScript 1.7 interpreter to 1.8.0rc1. That's the
latest stand-alone release I could find; I wasn't able to locate
non-browser versions of TraceMonkey or JaegerMonkey.

I also installed Chrome's V8 engine. It completely outclassed the
non-JIT SpiderMonkey, running the toBin() tests about 10x faster.


--
stefan
From: Richard Cornford on
Stefan Weiss wrote:
> On 07/07/10 06:06, RobG wrote:
>> On Jul 6, 10:54 pm, Richard Cornford wrote:
<snip>
>> However, your suggestion of adding high bits and
>> subtracting 3 had me tossed for a while but then I realised
>> it's the same as right-shift then add 5. That didn't help
>> (or hinder) IE but did help Firefox.
>
> That would appear to depend on the Firefox version. Avoiding the
> if-statement actually improved performance in my stand-alone
> SpiderMonkey versions (tested 1.7.0 and 1.8.0rc1).
<snip>

There is a possibility that hardware (CPU type, cache size, memory
speed, effective memory bus width, etc.) could be having an influence.
It was once observed here that, running on the same browser/version and
OS, string comparison was faster than the equivalent simple regular
expression test running on a PIII, but the regular expression
(unexpectedly) proved faster on a P4. This has been attributed to the
influence of longer pipeline on the P4. Regardless of the specific
explanation in that case, it doe demonstrate that hardware can reverse
the outcome of comparative timings when dealing with javascript.

Richard.

From: RobG on
On Jul 8, 12:00 am, Stefan Weiss <krewech...(a)gmail.com> wrote:
> On 07/07/10 06:06, RobG wrote:
[...]
> > However, your suggestion of adding high bits and
> > subtracting 3 had me tossed for a while but then I realised it's the
> > same as right-shift then add 5. That didn't help (or hinder) IE but
> > did help Firefox.
>
> That would appear to depend on the Firefox version. Avoiding the
> if-statement actually improved performance in my stand-alone
> SpiderMonkey versions (tested 1.7.0 and 1.8.0rc1). The JS engine in
> current Firefox versions may give different results, possibly as a
> result of JIT compilation. On the other hand, the trunk version of V8
> also runs the code (marginally) faster when there is no "if" statement
> in the "for" loop.
[...]
> I think we're well into the area of engine specific optimizations now.

Yes. I ran several versions in Safari 5.0 and they were all about the
same, say +/- 5%. All of them were about 5x faster than the fastest in
Firefox 3.5.

I also tested against one of the BigInt.js libraries[1] mentioned in
another thread. It has a general conversion to any base that takes
twice as long as the bespoke version here for conversion to base 2.
Unfortunately the one I used is restricted to strings of 16 digits or
less, so it's not really suited to the library's goal of being "a
bigInt library for arbitrary-precision integers".


1. There appears to be more than one of these, I used this one:
<URL: http://www.leemon.com/crypto/BigInt.js >

But later I found this one:
<URL: http://www.onicos.com/staff/iz/amuse/javascript/expert/BigInt.txt
>


--
Rob