Prev: Firefox cache issue
Next: FAQ Topic - How do I format a Number as a String with exactly 2 decimal places? (2010-05-27)
From: Dr J R Stockton on 4 Jun 2010 16:06 In comp.lang.javascript message <3200480.OGUtdWV9SE(a)PointedEars.de>, Thu, 3 Jun 2010 09:43:22, Thomas 'PointedEars' Lahn <PointedEars(a)web.de> posted: >Dr J R Stockton wrote: > >> Thomas 'PointedEars' Lahn posted: >>> Dr J R Stockton wrote: >>>> Thomas 'PointedEars' Lahn posted: >>>>> Dr J R Stockton wrote: >>>>>> That indeed gives -3.141592653589793; but use instead Math.PI/10 and >>>>>> it gives 0.3141592653589793. Whenever parseInt(s, 16) gives a zero, >>>>>> your code will give a positive result. >>>>> ACK, thanks. ISTM that checking whether the first character of the >>>>> representation is a `-' solves this particular problem. Again, largely >>>>> untested: >>>> Entirely unnecessary. Just use the sign of the number 'i'. >>> I had: (i < 0 ? -1 : 1). But *you* pointed out to me that `i' would be 0 >>> if the absolute value of the represented number would be less than 1. >> >> Indeed you did; and, as you know, your test does not suffice, since it >> does not always give the sign of the number 'i'. >> >> I repeat - you need to use the sign of the number 'i'. > >Since the value of `i' is the return value of `parseInt(s, base)' here, in >the border case that `s' represents a number which absolute value is less >than 1, `i' has no sign or IOW its sign is always positive. It is therefore >hard to see how its sign could be useful. > >> You so frequently insist that others search the archives; you should do >> so yourself. The matter was referred to sometime in the years 200x. >> Careful reading of ECMA 262 3/5 could help you. > >Instead of making yet another lame attempt at an ad hominem attack, and >speaking in riddles, you could just have said how you would do it. Suppose, >just suppose, that I do exactly what you just did, then you are no better >than me. This should give you pause. Telling you to search the archives has evidently, as intended, annoyed you; you have found it to be not really helpful. Perhaps, therefore, you will now abandon the practice of making such a recommendation to others as frequently as you have been doing. But I doubt it. You are really being perversely obtuse this week. The answer is on my Web site, in the obvious place. AND it is also in the article which I posted to this thread just a week ago today. AND it is fairly obvious from what ECMA 262 says. Granted, the additional code in my article to handle the case of strings starting '-0.' is not easy to see, since it is only two characters; but there's a whole sentence of clue directly preceding the code. But, really, you should be aware that, apart from the case of NaN(s), the IEEE Double format CANNOT represent an unsigned quantity. Of course. parseInt(+0.5) == parseInt(-0.5) // -> true but 1/parseInt(+0.5) == 1/parseInt(-0.5) // -> false My reading of ECMA 262 is that parseInt is required firstly to remove whitespace and set /sign/ to 1, then if the string starts '-' set /sign/ to -1. It then processes the rest to get a (non-negative) /number/, and returns /sign/ times /number/. Now read ECMA 11.5.2. Function parseInt always returns an appropriately-SIGNED Number. AFAIK, parseInt cannot return an infinity. In the present case, we expect a finite, and signed, value in 'i' - and the sign can ALWAYS be determined by comparing a zero (of either sign) with the reciprocal of 'i'. I chose to use (2*(1/A>0)-1) where you have used (i < 0 ? -1 : 1) but you could use (1/i < 0 ? -1 : 1). Again, 'i' is a bad choice of identifier where you do not know in what font it will be read (and I is not much better). Only in strings can JavaScript represent an unsigned zero. IIRC, the case of the nasty format shown by -.12345 can be handled with a RegExp replace of (\d*)\. by 0$1 . ASIDE, as .toString(radix) is also about string/number interconversion : ISTM that looking, for each radix 2 to 36, at the last character of Math.random().toString(radix), very repeatedly, the set of characters obtained is not the same in any 2 of the 5 browsers on this PC - Firefox 3.0.19 alone giving the expected "any non- zero digit in the base". My js-randm.htm refers. -- (c) John Stockton, nr London UK. ?@merlyn.demon.co.uk Turnpike v6.05 MIME. Grandson-Of-RFC1036 is released. RFC 5536 Netnews Article Format is a subset of Internet Message Format which is described in RFC 5532. The RFCs are read together to determine standard Netnews article format.
From: Dr J R Stockton on 15 Jun 2010 18:41 In comp.lang.javascript message <1J7cUsISsBAMFwdk(a)invalid.uk.co.demon.me rlyn.invalid>, Fri, 28 May 2010 20:35:46, Dr J R Stockton <reply1021(a)merlyn.demon.co.uk> posted: >ISTM that parseInt("-0", 16) does indeed return -0. > > S = "-45.6" > A = parseInt(S, 16) > if (S = S.split(".")[1]) > A += (2*(1/A>0)-1) * parseInt(S, 16) / Math.pow(16, S.length) > // -> -69.375 > >That is undertested. A number string should always have a digit before >its basal separator if any. Have no non-digit terminator. This accepts zero or more digits before the point, and trailing non- digits, and ludicrously long input fractions. function parsFlotB(S, R) { S = S.replace(/(\d*\.)/, "0$1") // ensure \d+ 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 } -- (c) John Stockton, nr London UK. ?@merlyn.demon.co.uk Turnpike v6.05 MIME. Grandson-Of-RFC1036 is released. RFC 5536 Netnews Article Format is a subset of Internet Message Format which is described in RFC 5532. The RFCs are read together to determine standard Netnews article format.
From: Scott Sauyet on 16 Jun 2010 11:30 Dr J R Stockton wrote: > This accepts zero or more digits before the point, and trailing non- > digits, and ludicrously long input fractions. > > function parsFlotB(S, R) { > S = S.replace(/(\d*\.)/, "0$1") // ensure \d+ 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 } This still loses some possible precision. Try the first example at http://scott.sauyet.com/Javascript/Test/2010-06-16a/ (Doesn't work in IE, and I just can't bother to figure out why right now.) My technique is certainly inefficient, but it does seem to gain a digit or two of precision over the others presented. -- Scott
From: Dr J R Stockton on 17 Jun 2010 18:45 In comp.lang.javascript message <5583d1fb-9746-4999-9d7b-46a0e831bad5(a)e5 g2000yqn.googlegroups.com>, Wed, 16 Jun 2010 08:30:51, Scott Sauyet <scott.sauyet(a)gmail.com> posted: >Dr J R Stockton wrote: >> This accepts zero or more digits before the point, and trailing non- >> digits, and ludicrously long input fractions. >> >> function parsFlotB(S, R) { >> � S = S.replace(/(\d*\.)/, "0$1") // ensure \d+ 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 } > >This still loses some possible precision. Try the first example at > > http://scott.sauyet.com/Javascript/Test/2010-06-16a/ > >(Doesn't work in IE, and I just can't bother to figure out why right >now.) > >My technique is certainly inefficient, but it does seem to gain a >digit or two of precision over the others presented. I recommend monospace for input type=text , by CSS. I suggest that you show the result of the "parseFloat" in addition to the toString thereof; toString is clearly not reliable cross-browser for less popular radixes. Testing '0.fgr' to base 36 on your page, all but yours are perfect in Firefox 3.0.19. Try '0.fgr' to base 27 ! Try almost any fraction to a large odd base on Chrome; the "results" are unreasonably long in all four cases. Method Number.toString, used there for display, is untrustworthy. That is why the above code includes � S = S.substring(0, 99) . Your base-36 test uses "0.r3j6f0mqo4fr3j6f0m" which has 18 radical places, so the string has more resolution than an IEEE Double can give; that of course is why results are shorter. Your parseFraction seems to loop over all of the fraction digits, repeatedly dividing by base. That perhaps means repeated rounding errors, unless the JavaScript engine is unreasonably clever. Using parseInt on the fractional part should be better, since parseInt ought to be exact up to a result of 2^53. For such functions, it would be useful to have exact statements of how, with radix=10, they differ from ECMA 15.1.2.3 parseFloat (string). I guess all disallow ExponentPart. You and Jorge give NaN for a string such as "11.001 2*2=4". You accept "++55". Jorge, I think, crashes if not given a radical point. Mine gives 0 from ".", which is too tolerant. For meaningful tests of accuracy, ISTM essential to have a "master" parseFloat or a "master" "toString" which is absolutely accurate, using absolutely accurate arithmetic throughout. I have, via sig line 3, a programmable megadigit integer arithmetic package for bases 2 to 16, which might be useful here - Pascal/Delphi LONGCALC. If I were starting again, I could use bases 2 to 256 equally easily (apart from the representation as strings), but I don't fancy changing it now. OTOH, how about the following, which is intended to be evidently good without regard to speed, and expects proper input only :- function ExactPF(S, Rdx) { var J, L = 0, R = 0, RN = 1 S = S.split(".") var Int = S[0].split("") var Frc = S[1].split("") var Sgn = Int[0] == "-" ? -1 : +1 if (Sgn == -1) Int.shift(1) for (J = 0 ; J < Int.length ; J++) L = L * Rdx + parseInt(Int[J], Rdx) for (J = 0 ; J < Frc.length ; J++) { RN *= Rdx R = R * Rdx + parseInt(Frc[J], Rdx) } return Sgn * ( L + R/RN ) } // Consider case of L or R exceeding 2^53 Note that it uses parseInt only on single characters, which reduces the chance of error. I've nor found any; but it is practical to test parseInt with all bases and all single-character strings, but not with all multi-digit strings. -- (c) John Stockton, nr London UK. ?@merlyn.demon.co.uk DOS 3.3, 6.20; WinXP. Web <URL:http://www.merlyn.demon.co.uk/> - FAQqish topics, acronyms & links. PAS EXE TXT ZIP via <URL:http://www.merlyn.demon.co.uk/programs/00index.htm> My DOS <URL:http://www.merlyn.demon.co.uk/batfiles.htm> - also batprogs.htm.
From: Scott Sauyet on 19 Jun 2010 00:01
Dr J R Stockton wrote: > In comp.lang.javascript message <5583d1fb-9746-4999-9d7b-46a0e831bad5(a)e5 > g2000yqn.googlegroups.com>, Wed, 16 Jun 2010 08:30:51, Scott Sauyet > <scott.sau...(a)gmail.com> posted: >>Dr J R Stockton wrote: >> http://scott.sauyet.com/Javascript/Test/2010-06-16a/ > I suggest that you show the result of the "parseFloat" in addition to > the toString thereof; toString is clearly not reliable cross-browser for > less popular radixes. Good idea, it's in the latest version here: http://scott.sauyet.com/Javascript/Test/2010-06-18a/ as is a try-catch around the call to each function with some minimal error reporting. > Testing '0.fgr' to base 36 on your page, all but yours are perfect in > Firefox 3.0.19. Ahh, yes, I can see that mine can lose some precision with smaller inputs, while it gains some at larger length inputs. > Try '0.fgr' to base 27 ! That doesn't bother me much at least until I figure out how it's supposed to react to illegitimate input. If you try the same thing to base 10 you get other surprising results. > Try almost any fraction to a large odd base on Chrome; the "results" are > unreasonably long in all four cases. Method Number.toString, used there > for display, is untrustworthy. That is why the above code includes > S = S.substring(0, 99) . > > Your base-36 test uses "0.r3j6f0mqo4fr3j6f0m" which has 18 radical > places, so the string has more resolution than an IEEE Double can give; > that of course is why results are shorter. Right, but that does not mean that we can't get more exact results when we calculate with additional digits. I think maybe it would be best to combine these techniques. I have some ideas how, but have not yet tried to implement them as my brain is too fuzzy at the moment. (Midnight here.) Briefly, the idea would be to figure out and cache the maximum number of digits for each base that fit in 53 bits, use those for the most exact results, but then calculate a few digits further with the technique I used earlier. > Your parseFraction seems to loop over all of the fraction digits, > repeatedly dividing by base. That perhaps means repeated rounding > errors, unless the JavaScript engine is unreasonably clever. Using > parseInt on the fractional part should be better, since parseInt ought > to be exact up to a result of 2^53. Yes, but a IEEE754 number can sometimes still have a bit more precision than the result of that integer divided by the relevant power of the radix. > For such functions, it would be useful to have exact statements of how, > with radix=10, they differ from ECMA 15.1.2.3 parseFloat (string). I > guess all disallow ExponentPart. You and Jorge give NaN for a string > such as "11.001 2*2=4". You accept "++55". Jorge, I think, crashes if > not given a radical point. Mine gives 0 from ".", which is too > tolerant. I've added this test too. > For meaningful tests of accuracy, ISTM essential to have a "master" > parseFloat or a "master" "toString" which is absolutely accurate, using > absolutely accurate arithmetic throughout. > > I have, via sig line 3, a programmable megadigit integer arithmetic > package for bases 2 to 16, which might be useful here - Pascal/Delphi > LONGCALC. If I were starting again, I could use bases 2 to 256 equally > easily (apart from the representation as strings), but I don't fancy > changing it now. > > OTOH, how about the following, which is intended to be evidently good > without regard to speed, and expects proper input only :- > > function ExactPF(S, Rdx) { var J, L = 0, R = 0, RN = 1 > S = S.split(".") > var Int = S[0].split("") > var Frc = S[1].split("") > var Sgn = Int[0] == "-" ? -1 : +1 > if (Sgn == -1) Int.shift(1) > for (J = 0 ; J < Int.length ; J++) > L = L * Rdx + parseInt(Int[J], Rdx) > for (J = 0 ; J < Frc.length ; J++) { RN *= Rdx > R = R * Rdx + parseInt(Frc[J], Rdx) } > return Sgn * ( L + R/RN ) } // Consider case of L or R exceeding 2^53 > > Note that it uses parseInt only on single characters, which reduces the > chance of error. I've nor found any; but it is practical to test > parseInt with all bases and all single-character strings, but not with > all multi-digit strings. I've included it on the page without considering it thoroughly enough. I'll try to have another look over the weekend. A very interesting discussion! Thank you, -- Scott |