Hello everyone, This is an issue I've had awareness of going way back but it's the first time I've had to personally deal with it. I've done some research and found it addressed in a number of ways but given current time pressures and a huge list of to-dos, I thought I'd turn to the community for rapid advice to get me over the hump. The circumstances are summarized below in code and comments suitable for a playground. "This is the actual arithmetic I want to do... 0.09560268 - 0.005 = 0.09060268" "Evaluate this in a playground and you'll see this is what floating point arithmetic produces --the first two are wrong, every one after is correct" 0.09560268 - 0.005 "=> 0.09060267999999999". 0.0956026 - 0.005 "=> 0.09060259999999999". 0.095602 - 0.005 "=> 0.090602". 0.09560 - 0.005 "=> 0.0906". 0.0956 - 0.005 "=> 0.0906". 0.095 - 0.005 "=> 0.09". "This workaround works" 0.09560268 - 0.005 roundTo: 0.00000001 "=> 0.09060268". "but one has to compute number of digits of precision and according to study above, it's not necessary at precisions >= 0.000001" "What are other suggestions, workarounds or approaches community has?" "THANKS!" |
Whats wrong with #roundedTo: ?
If you actually need it calculated to 8 significant digits then you can use scaled decimals e.g. (0.09560268 asScaledDecimal - 0.005 asScaledDecimal) see https://0.30000000000000004.com for more about why floating point math does this sometimes Donald Howard wrote > Hello everyone, > > This is an issue I've had awareness of going way back but it's the first > time I've had to personally deal with it. I've done some research and > found it addressed in a number of ways but given current time pressures > and > a huge list of to-dos, I thought I'd turn to the community for rapid > advice > to get me over the hump. The circumstances are summarized below in code > and comments suitable for a playground. > > "This is the actual arithmetic I want to do... > 0.09560268 - 0.005 = 0.09060268" > > "Evaluate this in a playground and you'll see this is what > floating point arithmetic produces --the first two are wrong, every one > after is correct" > 0.09560268 - 0.005 "=> 0.09060267999999999". > 0.0956026 - 0.005 "=> 0.09060259999999999". > 0.095602 - 0.005 "=> 0.090602". > 0.09560 - 0.005 "=> 0.0906". > 0.0956 - 0.005 "=> 0.0906". > 0.095 - 0.005 "=> 0.09". > > "This workaround works" > 0.09560268 - 0.005 roundTo: 0.00000001 "=> 0.09060268". > "but one has to compute number of digits of precision > and according to study above, it's not necessary at > precisions >= 0.000001" > > "What are other suggestions, workarounds or approaches community has?" > "THANKS!" > > > Don Howard > > <https://objectguild.com> > *Founding Member* > dhoward@ > +31653139744 > (US) 651-253-7024 -- Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html |
In reply to this post by Donald Howard
On 12/01/2020 21:13, Donald Howard wrote:
> "Evaluate this in a playground and you'll see this is what > floating point arithmetic produces --the first two are wrong, every > one after is correct" > 0.09560268 - 0.005 "=> 0.09060267999999999". > 0.0956026 - 0.005 "=> 0.09060259999999999". > 0.095602 - 0.005 "=> 0.090602". Wrong in what sense? I am rather sure that the computation is done correctly, meaning that it follows the rules defined by IEEE standard 754. I am also relatively sure (meaning a bit less) that the conversion of the binary result to a decimal output string is correct, in the sense that it applies today's preferred practice of ensuring round-trip equality. So you probably mean that the result is no what you expected. The main point to be clear about is if you care about the binary value or about its decimal representation as a string. If you need the internal value to be exact, you have to switch to something other than binary floats - scaled decimals might work for you. If you only want nice-looking outputs, you can probably get what you want using roundTo: applied to the result. Cheers, Konrad. |
In reply to this post by Donald Howard
You say that the first two results are wrong. No. The first two results are *exactly* what they are supposed to be. Floating point arithmetic is NOT mathematical real arithmetic. The floating point *numbers* are exact rational numbers of the form n * 2^k where n is a (bounded) integer and k is a (bounded) integer. If you enter a number that is not of this form (and within bounds) you will get a *different* number that *is* of that form and is close. The floating point *operations* in IEEE arithmetic are exact when and only when the result is a number of that form (and within bounds), otherwise the result is *approximate*. If you want exact arithmetic on numbers of the form n * 10^k, ANSI Smalltalk provides the ScaledDecimal class, which, sigh, Squeak and Pharo interpret as "rational number *printed* in decimal form but not *computed* using decimal arithmetic", so that can give extremely confusing results too. So what you are going to have to do is use rational numbers, which are in Smalltalk the union of Integer and Fraction. On Mon, 13 Jan 2020 at 09:13, Donald Howard <[hidden email]> wrote:
|
On Mon, 13 Jan 2020 at 19:42, Richard O'Keefe <[hidden email]> wrote:
How could this be improved?
I'm interested to understand what is the benefit of doing it this way. Anyone know history of it? cheers -ben
|
In reply to this post by Paul DeBruicker
> On 13 Jan 2020, at 00:28, Paul DeBruicker <[hidden email]> wrote: > > Whats wrong with #roundedTo: ? > > > If you actually need it calculated to 8 significant digits then you can use > scaled decimals e.g. > > (0.09560268 asScaledDecimal - 0.005 asScaledDecimal) The weird thing is though, that is still does not behave 100% as one would expect (i.e. it does not seem exact). 0.09560268 asScaledDecimal - 0.005 asScaledDecimal. "0.09060268s8" 0.09560268 asScaledDecimal - 0.005 asScaledDecimal = 0.09060268 asScaledDecimal. "false" 0.09560268 asScaledDecimal - 0.005 asScaledDecimal = 0.09060268s8. "false" The internal fractions are not the same ... While (9560268 / 1e8) - (5 / 1e3) = (9060268 / 1e8). "true" So, is the equality test of ScaledDecimal correct ? > see https://0.30000000000000004.com for more about why floating point math > does this sometimes > > > > > > Donald Howard wrote >> Hello everyone, >> >> This is an issue I've had awareness of going way back but it's the first >> time I've had to personally deal with it. I've done some research and >> found it addressed in a number of ways but given current time pressures >> and >> a huge list of to-dos, I thought I'd turn to the community for rapid >> advice >> to get me over the hump. The circumstances are summarized below in code >> and comments suitable for a playground. >> >> "This is the actual arithmetic I want to do... >> 0.09560268 - 0.005 = 0.09060268" >> >> "Evaluate this in a playground and you'll see this is what >> floating point arithmetic produces --the first two are wrong, every one >> after is correct" >> 0.09560268 - 0.005 "=> 0.09060267999999999". >> 0.0956026 - 0.005 "=> 0.09060259999999999". >> 0.095602 - 0.005 "=> 0.090602". >> 0.09560 - 0.005 "=> 0.0906". >> 0.0956 - 0.005 "=> 0.0906". >> 0.095 - 0.005 "=> 0.09". >> >> "This workaround works" >> 0.09560268 - 0.005 roundTo: 0.00000001 "=> 0.09060268". >> "but one has to compute number of digits of precision >> and according to study above, it's not necessary at >> precisions >= 0.000001" >> >> "What are other suggestions, workarounds or approaches community has?" >> "THANKS!" >> >> >> Don Howard >> >> <https://objectguild.com> >> *Founding Member* > >> dhoward@ > >> +31653139744 >> (US) 651-253-7024 > > > > > > -- > Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html |
Hi Sven,
if you dont transform floats, that are not always exactly what they seem to be, you get what you would expect: 0.09560268s8 - 0.005s8 = 0.09060268s8. "true" "of course" a:=0.09560268 asScaledDecimal. 0.09560268s8=a . "false" "you could eg do this:" b:=a roundTo: 0.00000001s8. "then" 0.09560268s8=b . "true" but if you do something like this generally in Float>>asScaledDecimal you could loose some precision that could be unwanted in some other circumstances (or perhaps not). werner On 1/13/20 2:13 PM, Sven Van Caekenberghe wrote: > 0.09560268 asScaledDecimal - 0.005 asScaledDecimal = 0.09060268s8. "false" |
Ah, yes, yes, sending #asScaledDecimal to a Float is too late, the precision is already lost. It makes total sense now.
Thank you, Werner, for the explanation. > On 13 Jan 2020, at 16:13, werner kassens <[hidden email]> wrote: > > Hi Sven, > > if you dont transform floats, that are not always exactly what they seem > to be, you get what you would expect: > 0.09560268s8 - 0.005s8 = 0.09060268s8. "true" > "of course" > a:=0.09560268 asScaledDecimal. > 0.09560268s8=a . "false" > "you could eg do this:" > b:=a roundTo: 0.00000001s8. > "then" > 0.09560268s8=b . "true" > > but if you do something like this generally in Float>>asScaledDecimal > you could loose some precision that could be unwanted in some other > circumstances (or perhaps not). > > werner > > On 1/13/20 2:13 PM, Sven Van Caekenberghe wrote: >> 0.09560268 asScaledDecimal - 0.005 asScaledDecimal = 0.09060268s8. "false" > |
In reply to this post by Donald Howard
On Sun, Jan 12, 2020 at 02:13:00PM -0600, Donald Howard wrote:
> "What are other suggestions, workarounds or approaches community has?" This is a good read on the subject: https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html Title is "What every computer scientist should know about floating point arithmetic". Pierce |
In reply to this post by Richard O'Keefe
On Tue, Jan 14, 2020 at 12:41:51AM +1300, Richard O'Keefe wrote:
> Squeak and Pharo interpret as "rational number *printed* in > decimal form but not *computed* using decimal arithmetic", so > that can give extremely confusing results too. Can you expand on this please? For money-related calculations I have been using Hernán Morales Durand's FixedDecimal: https://github.com/hernanmd/FixedDecimal. Pierce |
In reply to this post by Pierce Ng-3
HI, I would upgrade that "a good read" to a critical read if you do anything with floating point. Floating point numbers are not real numbers but rather a small subset, and, have all kinds of funny properties. But we use them just like real numbers and that's what catches us out. cheers bruce 14 January 2020 03:05 Pierce Ng <[hidden email]> wrote:
|
Thanks, everyone, for the great input. I will continue to work thru the background readings and recommendations you graciously supplied and go from there. Best regards, - Don On Tue, 14 Jan 2020 at 07:04, Bruce O'Neel <[hidden email]> wrote:
|
Free forum by Nabble | Edit this page |