Rounding in Floats

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
40 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Steffen Märcker
Typo: FP should read floating point and not fixed point.

Steffen Märcker schrieb am Dienstag, 15. Juni 2021 08:47:24 (+02:00):

> Have you considered using fixed-point arithmetic? For example:
> 7.1s2 roundTo: 0.1s2
>
> The rule of thumb I stick to is to use FP only if I know the inaccuracies
> won't bite me. Funny enough, both 7.1 and 0.1 are already not accurately
> representable as floats. (And by coincidence, I prepared exam questions
> about floats for my students yesterday. )
>
> Kind regards,
> Steffen
>
>
> Konrad Hinsen schrieb am Dienstag, 15. Juni 2021 07:02:30 (+02:00):
>
> > On 15/06/2021 01:03, Esteban Maringolo wrote:
> > > Sure, but what initiated this thread was a reference to roundTo: 0.1
> > > which produced a "wrong" output.
> > >
> > > (9.1 + (-2.0)) roundTo: 0.1 "=> 7.1000000000000005"
> > > 7.1 roundTo: 0.1 "=> 7.1000000000000005"
> > >
> > > However, at this point I know that Pharo "does the right, raw, thing"
> > > (at least compared to other mainstream languages), but it still
> > > produces a surprise effect.
> >
> > That's the "floating point surprise" that everyone has at some point, no
> matter the language and runtime system. If that surprise is a problem for
> you, are you sure that floating-point arithmetic is what you really want?
> Maybe your needs are better served with integers and fractions.
> >
> >
> > Konrad.
> >
> >
> --
> Gesendet mit Vivaldi Mail. Laden Sie Vivaldi kostenlos von vivaldi.com
> herunter.
>
--
Gesendet mit Vivaldi Mail. Laden Sie Vivaldi kostenlos von vivaldi.com herunter.
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Richard O'Keefe
In reply to this post by Esteban A. Maringolo
Whether 6.7 - 2.2 gives 4.5 or something else is a subtle issue,
starting with the fact
that 6.7 and 2.2 cannot be represented precisely in floating-point
format.  In this particular case, the errors happen to match up.
What you *can* rely on in Smalltalk is that (67/10) - (22/10) = (9/2).

Let's just consider h * s / 113 + (r - p).
If h, s, r, and p are all exact numbers (Integers or Fractions) the
result will be an exact number.
So what kinds of number are h, s, r, and p, and *why* are they
like that?  If you are reading them from a file, consider that
Fraction readFrom: '6.7' readStream >>> (67/10)

Is there a specification you could show us?  Some test data?


On Tue, 15 Jun 2021 at 14:24, Esteban Maringolo <[hidden email]> wrote:

>
> On Mon, Jun 14, 2021 at 10:37 PM Richard O'Keefe <[hidden email]> wrote:
>
> > For what it's worth, I expected ScaledDecimal to act like fixed point
> > arithmetic, and implemented it that way in my own Smalltalk library, where
> > two ScaledDecimals *do* print the same if and only if they are numerically
> > exactly the same.
> > What Squeak and Pharo do is exceedingly odd: a ScaledDecimal
> > is an exact rational number (Integer or Fraction) combined with a precision that
> > is used for printing, not for calculation.
>
> I pointed out before the weird behavior of ScaledDecimals in Pharo,
> two ScaledDecimal that print the same are not equal. Giving some weird
> comparison issues as result.
>
> > There really isn't any principle of least surprise when it comes to floating-
> > point arithmetic.  It's full of surprises and edge cases.  Excel in particular
> > is notorious for messing up due to trying to pretend all is well.
> > In this particular case, the exact result is 4.5
>
> Well... for the end user, it produces what they'd expect. So it might
> mess up, but in a spreadsheet 6.7 - 2.2 should give 4.5.
>
> > There are at least three rules for rounding such numbers: rounding out (5),
> > rounding in (4), and rounding in [banker'salgorithm] (4 here, but 5.5 -> 6).
> > So you are pushing up against an edge case for exact hand calculation!
>
> I implemented a Float>>#roundedHandicap method that does something
> like the banking algorithm, but rounds positive numbers towards the
> next integer and negative numbers towards zero.
> E.g.
> 0.49 -> 0
> 0.5 -> 1
> -0.5 -> 0
> -0.51 -> -1
>
> Nothing uses more than one decimal, so a ScaledDecimal would work but
> the specification says that it should use all possible precision in
> intermediate calculations, so I cannot use it.
>
> > I think you need to re-express your entire calculation to use exact arithmetic.
>
> I really don't know how to do this, any pointers?
>
> Nothing is more straightforward than addition and subtraction to me,
> 6.7 - 2.2 is the simplest it can get.
>
> The common formula here is: h * s / 113 + (r - p), but in this
> particular case s was 113 so it removed the "troubling" part.
>
> > That or get agreement on "de minimis non curat lex".
>
> I had to search for that expression. Now I know, I agree.
>
> Regards,
>
> Esteban A. Maringolo
>
>
>
> >
> >
> > On Tue, 15 Jun 2021 at 08:45, Esteban Maringolo <[hidden email]> wrote:
> > >
> > > I'm coming back to this because I've been bitten by these floating
> > > points things again.
> > >
> > > If in Pharo [1] you do:
> > > a := 6.7 + (32.8 - 35)
> > >
> > > It will produce:
> > > 4.499999999999997
> > >
> > > Which, when rounded, will produce 4.
> > >
> > > In other places [2] I do the same simple addition and subtraction it
> > > produces 4.5, that when rounded will produce 5.
> > >
> > > I know now that Pharo doesn't lie to me while other systems do, and
> > > all that Richard pointed to before.
> > >
> > > The issue here is that I'm following some calculation formula that was
> > > defined in some of the "other" systems, and so when I follow such a
> > > formula I get these edgy cases where my system produces a different
> > > output.
> > >
> > > In this case the formula is for golf handicap calculations, and it
> > > caused my system to give 4 instead of 5 to a player, resulting in
> > > giving the first place to a player other than the one deserved.
> > > It was no big deal (it's not The Masters), but these cases appear from
> > > time to time.
> > >
> > > Is there any way to "configure" the floating point calculation to
> > > behave as the "other systems"?
> > >
> > > What is the best way to anticipate these situations, am I the only one
> > > being bitten by these issues?
> > >
> > > Thanks in advance for any hints about these problems.
> > >
> > >
> > > Best regards,
> > >
> > > [1] Dolphin Smalltalk, JS, Python, Ruby, Dart produces the same output as Pharo.
> > > [2] VisualWorks, VAST, Excel, VB and all calculators I tried
> > >
> > >
> > >
> > > Esteban A. Maringolo
> > >
> > > On Tue, Sep 8, 2020 at 12:45 AM Esteban Maringolo <[hidden email]> wrote:
> > > >
> > > > On Tue, Sep 8, 2020 at 12:16 AM Richard O'Keefe <[hidden email]> wrote:
> > > > >
> > > > > "7.1 roundTo: 0.1 should return 7.1"
> > > > > You're still not getting it.
> > > >
> > > > I was until Konrad explained it.
> > > >
> > > > > Binary floating point CANNOT represent either of those numbers.
> > > > > You seem to be assuming that Pharo is making some mistake.
> > > > > It isn't.  All it is doing is refusing to lie to you.
> > > > <snip>
> > > > > The systems that print 7.1 are LYING to you,
> > > > > and Pharo is not.
> > > >
> > > > I'm not assuming a mistake from Pharo, I had a wrong expectation what
> > > > to get if I round to that precision.
> > > > I don't know whether other systems lie or simply fulfill user
> > > > expectations, if you send the #roundTo: to a float, I did expect to
> > > > get a number with the same precision.
> > > > That is my expectation as a user. As in the other thread I expected
> > > > two scaled decimals that are printed equal to also be compared as
> > > > equal  (which they don't).
> > > >
> > > > Whether there is a good reason for those behaviors is beyond my
> > > > current comprehension, but it certainly doesn't follow the "principle
> > > > of least surprise".
> > > >
> > > > In any case, the method proposed by Tomohiro solved my issues.
> > > >
> > > > Regards,
> > > >
> > > > Esteban A. Maringolo
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Esteban A. Maringolo
In reply to this post by Steffen Märcker
Well... fixed point numbers (aka ScaledDecimals) had their issues as well.

And in this particular case, it might cause issues, since the "full
precision" (whatever that means) must be retained when using such a
number to derive another one.

E.g. in my case:
ch := 6.7 + (32.8 - 35). "course handicap"
allowance := 0.85. "85%"
ph := (ch * allowance) roundedHandicap. "playing handicap"

The #roundedHandicap is like #rounded but rounds -0.5 towards -1
instead of toward 0.

My first approach was to use fixed points, but then I hit issues where
some results also produced "wrong" numbers (according to the
reference), also in Pharo some ScaledDecimals compare as different
even when they're printed as the same (which is not correct, IMO).

I just tested using everything as fractions and converting to float
(or ScaledDecimal) at the very last step and it produces the expected
result. I'll review and convert whatever is needed (including database
fields) to work with this. The good thing is that all the involved
numbers only have one decimal as much.

Thanks!

Esteban A. Maringolo

On Tue, Jun 15, 2021 at 3:48 AM Steffen Märcker <[hidden email]> wrote:

>
> Have you considered using fixed-point arithmetic? For example:
> 7.1s2 roundTo: 0.1s2
>
> The rule of thumb I stick to is to use FP only if I know the inaccuracies
> won't bite me. Funny enough, both 7.1 and 0.1 are already not accurately
> representable as floats. (And by coincidence, I prepared exam questions
> about floats for my students yesterday. )
>
> Kind regards,
> Steffen
>
>
> Konrad Hinsen schrieb am Dienstag, 15. Juni 2021 07:02:30 (+02:00):
>
>  > On 15/06/2021 01:03, Esteban Maringolo wrote:
>  > > Sure, but what initiated this thread was a reference to roundTo: 0.1
>  > > which produced a "wrong" output.
>  > >
>  > > (9.1 + (-2.0)) roundTo: 0.1 "=> 7.1000000000000005"
>  > > 7.1 roundTo: 0.1 "=> 7.1000000000000005"
>  > >
>  > > However, at this point I know that Pharo "does the right, raw, thing"
>  > > (at least compared to other mainstream languages), but it still
>  > > produces a surprise effect.
>  >
>  > That's the "floating point surprise" that everyone has at some point, no
> matter the language and runtime system. If that surprise is a problem for
> you, are you sure that floating-point arithmetic is what you really want?
> Maybe your needs are better served with integers and fractions.
>  >
>  >
>  > Konrad.
>  >
>  >
> --
> Gesendet mit Vivaldi Mail. Laden Sie Vivaldi kostenlos von vivaldi.com
> herunter.
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Esteban A. Maringolo
In reply to this post by Richard O'Keefe
Hi Richard,

> Whether 6.7 - 2.2 gives 4.5 or something else is a subtle issue,
> starting with the fact
> that 6.7 and 2.2 cannot be represented precisely in floating-point
> format.  In this particular case, the errors happen to match up.
> What you *can* rely on in Smalltalk is that (67/10) - (22/10) = (9/2).
>
> Let's just consider h * s / 113 + (r - p).
> If h, s, r, and p are all exact numbers (Integers or Fractions) the
> result will be an exact number.
> So what kinds of number are h, s, r, and p, and *why* are they
> like that?  If you are reading them from a file, consider that
> Fraction readFrom: '6.7' readStream >>> (67/10)

I'm not reading them from a file, although some numbers come from an
API as a JS number (float).

Using Fractions wherever possible seems to be the right solution to
this. And given that I already know the precision (it's always 1
decimal as much) I can turn everything into that and store them as
DECIMAL in the database.

I'm reluctant to perform such a change because everything has been
working fine and this is just an edge case, but I think it is the
right thing to do.

> Is there a specification you could show us?

There is no specification other than the formulas:
https://www.usga.org/content/usga/home-page/handicapping/roh/2020-rules-of-handicapping.html

Section III -> Rules 6.1 and 6.2

There is only a remark after 6.1b that you must retain full precision
in intermediate calculations.
"the full calculated value is retained and rounding occurs only after
the Playing Handicap calculation."

> Some test data?

Not that I could find, I've been adding cases to my tests to assert it
behaves properly.

Thanks!


Esteban A. Maringolo

On Tue, Jun 15, 2021 at 4:13 AM Richard O'Keefe <[hidden email]> wrote:
>

>
>
> On Tue, 15 Jun 2021 at 14:24, Esteban Maringolo <[hidden email]> wrote:
> >
> > On Mon, Jun 14, 2021 at 10:37 PM Richard O'Keefe <[hidden email]> wrote:
> >
> > > For what it's worth, I expected ScaledDecimal to act like fixed point
> > > arithmetic, and implemented it that way in my own Smalltalk library, where
> > > two ScaledDecimals *do* print the same if and only if they are numerically
> > > exactly the same.
> > > What Squeak and Pharo do is exceedingly odd: a ScaledDecimal
> > > is an exact rational number (Integer or Fraction) combined with a precision that
> > > is used for printing, not for calculation.
> >
> > I pointed out before the weird behavior of ScaledDecimals in Pharo,
> > two ScaledDecimal that print the same are not equal. Giving some weird
> > comparison issues as result.
> >
> > > There really isn't any principle of least surprise when it comes to floating-
> > > point arithmetic.  It's full of surprises and edge cases.  Excel in particular
> > > is notorious for messing up due to trying to pretend all is well.
> > > In this particular case, the exact result is 4.5
> >
> > Well... for the end user, it produces what they'd expect. So it might
> > mess up, but in a spreadsheet 6.7 - 2.2 should give 4.5.
> >
> > > There are at least three rules for rounding such numbers: rounding out (5),
> > > rounding in (4), and rounding in [banker'salgorithm] (4 here, but 5.5 -> 6).
> > > So you are pushing up against an edge case for exact hand calculation!
> >
> > I implemented a Float>>#roundedHandicap method that does something
> > like the banking algorithm, but rounds positive numbers towards the
> > next integer and negative numbers towards zero.
> > E.g.
> > 0.49 -> 0
> > 0.5 -> 1
> > -0.5 -> 0
> > -0.51 -> -1
> >
> > Nothing uses more than one decimal, so a ScaledDecimal would work but
> > the specification says that it should use all possible precision in
> > intermediate calculations, so I cannot use it.
> >
> > > I think you need to re-express your entire calculation to use exact arithmetic.
> >
> > I really don't know how to do this, any pointers?
> >
> > Nothing is more straightforward than addition and subtraction to me,
> > 6.7 - 2.2 is the simplest it can get.
> >
> > The common formula here is: h * s / 113 + (r - p), but in this
> > particular case s was 113 so it removed the "troubling" part.
> >
> > > That or get agreement on "de minimis non curat lex".
> >
> > I had to search for that expression. Now I know, I agree.
> >
> > Regards,
> >
> > Esteban A. Maringolo
> >
> >
> >
> > >
> > >
> > > On Tue, 15 Jun 2021 at 08:45, Esteban Maringolo <[hidden email]> wrote:
> > > >
> > > > I'm coming back to this because I've been bitten by these floating
> > > > points things again.
> > > >
> > > > If in Pharo [1] you do:
> > > > a := 6.7 + (32.8 - 35)
> > > >
> > > > It will produce:
> > > > 4.499999999999997
> > > >
> > > > Which, when rounded, will produce 4.
> > > >
> > > > In other places [2] I do the same simple addition and subtraction it
> > > > produces 4.5, that when rounded will produce 5.
> > > >
> > > > I know now that Pharo doesn't lie to me while other systems do, and
> > > > all that Richard pointed to before.
> > > >
> > > > The issue here is that I'm following some calculation formula that was
> > > > defined in some of the "other" systems, and so when I follow such a
> > > > formula I get these edgy cases where my system produces a different
> > > > output.
> > > >
> > > > In this case the formula is for golf handicap calculations, and it
> > > > caused my system to give 4 instead of 5 to a player, resulting in
> > > > giving the first place to a player other than the one deserved.
> > > > It was no big deal (it's not The Masters), but these cases appear from
> > > > time to time.
> > > >
> > > > Is there any way to "configure" the floating point calculation to
> > > > behave as the "other systems"?
> > > >
> > > > What is the best way to anticipate these situations, am I the only one
> > > > being bitten by these issues?
> > > >
> > > > Thanks in advance for any hints about these problems.
> > > >
> > > >
> > > > Best regards,
> > > >
> > > > [1] Dolphin Smalltalk, JS, Python, Ruby, Dart produces the same output as Pharo.
> > > > [2] VisualWorks, VAST, Excel, VB and all calculators I tried
> > > >
> > > >
> > > >
> > > > Esteban A. Maringolo
> > > >
> > > > On Tue, Sep 8, 2020 at 12:45 AM Esteban Maringolo <[hidden email]> wrote:
> > > > >
> > > > > On Tue, Sep 8, 2020 at 12:16 AM Richard O'Keefe <[hidden email]> wrote:
> > > > > >
> > > > > > "7.1 roundTo: 0.1 should return 7.1"
> > > > > > You're still not getting it.
> > > > >
> > > > > I was until Konrad explained it.
> > > > >
> > > > > > Binary floating point CANNOT represent either of those numbers.
> > > > > > You seem to be assuming that Pharo is making some mistake.
> > > > > > It isn't.  All it is doing is refusing to lie to you.
> > > > > <snip>
> > > > > > The systems that print 7.1 are LYING to you,
> > > > > > and Pharo is not.
> > > > >
> > > > > I'm not assuming a mistake from Pharo, I had a wrong expectation what
> > > > > to get if I round to that precision.
> > > > > I don't know whether other systems lie or simply fulfill user
> > > > > expectations, if you send the #roundTo: to a float, I did expect to
> > > > > get a number with the same precision.
> > > > > That is my expectation as a user. As in the other thread I expected
> > > > > two scaled decimals that are printed equal to also be compared as
> > > > > equal  (which they don't).
> > > > >
> > > > > Whether there is a good reason for those behaviors is beyond my
> > > > > current comprehension, but it certainly doesn't follow the "principle
> > > > > of least surprise".
> > > > >
> > > > > In any case, the method proposed by Tomohiro solved my issues.
> > > > >
> > > > > Regards,
> > > > >
> > > > > Esteban A. Maringolo
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Esteban A. Maringolo
In reply to this post by Sven Van Caekenberghe-2
Hi Sven,

I accidentally skipped this.

How is this different from the GRNumberPrinter?

Where is the code available?

Thanks!

Esteban A. Maringolo

On Mon, Jun 14, 2021 at 6:30 PM Sven Van Caekenberghe <[hidden email]> wrote:

>
> BTW, I recently wrote my own float printer, as an experiment.
>
> NeoJSONFloatPrinter lowPrecision print: a.
>
> => 4.5
>
> What I wanted was a human friendly, compact float printer, that tries to go for the shortest, simplest number. It prefers integers and goes to scientific notation when needed, while limiting the precision. Maybe it is interesting to look at the code.
>
> But I am far from an expert.
>
> > On 14 Jun 2021, at 23:23, Sven Van Caekenberghe <[hidden email]> wrote:
> >
> >
> >
> >> On 14 Jun 2021, at 22:44, Esteban Maringolo <[hidden email]> wrote:
> >>
> >> I'm coming back to this because I've been bitten by these floating
> >> points things again.
> >>
> >> If in Pharo [1] you do:
> >> a := 6.7 + (32.8 - 35)
> >>
> >> It will produce:
> >> 4.499999999999997
> >>
> >> Which, when rounded, will produce 4.
> >
> > But,
> >
> > a roundTo: 0.1 "=> 4.5"
> >
> >> In other places [2] I do the same simple addition and subtraction it
> >> produces 4.5, that when rounded will produce 5.
> >>
> >> I know now that Pharo doesn't lie to me while other systems do, and
> >> all that Richard pointed to before.
> >>
> >> The issue here is that I'm following some calculation formula that was
> >> defined in some of the "other" systems, and so when I follow such a
> >> formula I get these edgy cases where my system produces a different
> >> output.
> >>
> >> In this case the formula is for golf handicap calculations, and it
> >> caused my system to give 4 instead of 5 to a player, resulting in
> >> giving the first place to a player other than the one deserved.
> >> It was no big deal (it's not The Masters), but these cases appear from
> >> time to time.
> >>
> >> Is there any way to "configure" the floating point calculation to
> >> behave as the "other systems"?
> >>
> >> What is the best way to anticipate these situations, am I the only one
> >> being bitten by these issues?
> >>
> >> Thanks in advance for any hints about these problems.
> >>
> >>
> >> Best regards,
> >>
> >> [1] Dolphin Smalltalk, JS, Python, Ruby, Dart produces the same output as Pharo.
> >> [2] VisualWorks, VAST, Excel, VB and all calculators I tried
> >>
> >>
> >>
> >> Esteban A. Maringolo
> >>
> >> On Tue, Sep 8, 2020 at 12:45 AM Esteban Maringolo <[hidden email]> wrote:
> >>>
> >>> On Tue, Sep 8, 2020 at 12:16 AM Richard O'Keefe <[hidden email]> wrote:
> >>>>
> >>>> "7.1 roundTo: 0.1 should return 7.1"
> >>>> You're still not getting it.
> >>>
> >>> I was until Konrad explained it.
> >>>
> >>>> Binary floating point CANNOT represent either of those numbers.
> >>>> You seem to be assuming that Pharo is making some mistake.
> >>>> It isn't.  All it is doing is refusing to lie to you.
> >>> <snip>
> >>>> The systems that print 7.1 are LYING to you,
> >>>> and Pharo is not.
> >>>
> >>> I'm not assuming a mistake from Pharo, I had a wrong expectation what
> >>> to get if I round to that precision.
> >>> I don't know whether other systems lie or simply fulfill user
> >>> expectations, if you send the #roundTo: to a float, I did expect to
> >>> get a number with the same precision.
> >>> That is my expectation as a user. As in the other thread I expected
> >>> two scaled decimals that are printed equal to also be compared as
> >>> equal  (which they don't).
> >>>
> >>> Whether there is a good reason for those behaviors is beyond my
> >>> current comprehension, but it certainly doesn't follow the "principle
> >>> of least surprise".
> >>>
> >>> In any case, the method proposed by Tomohiro solved my issues.
> >>>
> >>> Regards,
> >>>
> >>> Esteban A. Maringolo
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Sven Van Caekenberghe-2


> On 15 Jun 2021, at 16:19, Esteban Maringolo <[hidden email]> wrote:
>
> Hi Sven,
>
> I accidentally skipped this.
>
> How is this different from the GRNumberPrinter?

It is similar, but different (it does several things to produce cleaner numbers). Basically, when I produced certain JSON with floats that were results of calculations, I got these very long, ugly numbers. Forcing them to a certain precision always added trailing zeros and made them floats. I also wanted integers to be printed when possible (1 instead of 1.0, or 0 instead of 0.0). I also have automatic switching to scientific notation outside a certain range.

> Where is the code available?

It is part of NeoJSON, https://github.com/svenvc/NeoJSON

Keep in mind that this is just an unfinished experiment.

> Thanks!
>
> Esteban A. Maringolo
>
> On Mon, Jun 14, 2021 at 6:30 PM Sven Van Caekenberghe <[hidden email]> wrote:
>>
>> BTW, I recently wrote my own float printer, as an experiment.
>>
>> NeoJSONFloatPrinter lowPrecision print: a.
>>
>> => 4.5
>>
>> What I wanted was a human friendly, compact float printer, that tries to go for the shortest, simplest number. It prefers integers and goes to scientific notation when needed, while limiting the precision. Maybe it is interesting to look at the code.
>>
>> But I am far from an expert.
>>
>>> On 14 Jun 2021, at 23:23, Sven Van Caekenberghe <[hidden email]> wrote:
>>>
>>>
>>>
>>>> On 14 Jun 2021, at 22:44, Esteban Maringolo <[hidden email]> wrote:
>>>>
>>>> I'm coming back to this because I've been bitten by these floating
>>>> points things again.
>>>>
>>>> If in Pharo [1] you do:
>>>> a := 6.7 + (32.8 - 35)
>>>>
>>>> It will produce:
>>>> 4.499999999999997
>>>>
>>>> Which, when rounded, will produce 4.
>>>
>>> But,
>>>
>>> a roundTo: 0.1 "=> 4.5"
>>>
>>>> In other places [2] I do the same simple addition and subtraction it
>>>> produces 4.5, that when rounded will produce 5.
>>>>
>>>> I know now that Pharo doesn't lie to me while other systems do, and
>>>> all that Richard pointed to before.
>>>>
>>>> The issue here is that I'm following some calculation formula that was
>>>> defined in some of the "other" systems, and so when I follow such a
>>>> formula I get these edgy cases where my system produces a different
>>>> output.
>>>>
>>>> In this case the formula is for golf handicap calculations, and it
>>>> caused my system to give 4 instead of 5 to a player, resulting in
>>>> giving the first place to a player other than the one deserved.
>>>> It was no big deal (it's not The Masters), but these cases appear from
>>>> time to time.
>>>>
>>>> Is there any way to "configure" the floating point calculation to
>>>> behave as the "other systems"?
>>>>
>>>> What is the best way to anticipate these situations, am I the only one
>>>> being bitten by these issues?
>>>>
>>>> Thanks in advance for any hints about these problems.
>>>>
>>>>
>>>> Best regards,
>>>>
>>>> [1] Dolphin Smalltalk, JS, Python, Ruby, Dart produces the same output as Pharo.
>>>> [2] VisualWorks, VAST, Excel, VB and all calculators I tried
>>>>
>>>>
>>>>
>>>> Esteban A. Maringolo
>>>>
>>>> On Tue, Sep 8, 2020 at 12:45 AM Esteban Maringolo <[hidden email]> wrote:
>>>>>
>>>>> On Tue, Sep 8, 2020 at 12:16 AM Richard O'Keefe <[hidden email]> wrote:
>>>>>>
>>>>>> "7.1 roundTo: 0.1 should return 7.1"
>>>>>> You're still not getting it.
>>>>>
>>>>> I was until Konrad explained it.
>>>>>
>>>>>> Binary floating point CANNOT represent either of those numbers.
>>>>>> You seem to be assuming that Pharo is making some mistake.
>>>>>> It isn't.  All it is doing is refusing to lie to you.
>>>>> <snip>
>>>>>> The systems that print 7.1 are LYING to you,
>>>>>> and Pharo is not.
>>>>>
>>>>> I'm not assuming a mistake from Pharo, I had a wrong expectation what
>>>>> to get if I round to that precision.
>>>>> I don't know whether other systems lie or simply fulfill user
>>>>> expectations, if you send the #roundTo: to a float, I did expect to
>>>>> get a number with the same precision.
>>>>> That is my expectation as a user. As in the other thread I expected
>>>>> two scaled decimals that are printed equal to also be compared as
>>>>> equal  (which they don't).
>>>>>
>>>>> Whether there is a good reason for those behaviors is beyond my
>>>>> current comprehension, but it certainly doesn't follow the "principle
>>>>> of least surprise".
>>>>>
>>>>> In any case, the method proposed by Tomohiro solved my issues.
>>>>>
>>>>> Regards,
>>>>>
>>>>> Esteban A. Maringolo
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

khinsen
In reply to this post by Esteban A. Maringolo
On 15/06/2021 16:05, Esteban Maringolo wrote:
> And in this particular case, it might cause issues, since the "full
> precision" (whatever that means) must be retained when using such a
> number to derive another one.

To me, "full precision" means "no precision is ever lost in arithmetic
operations". If that's what you need, your choices are

1) Integers

2) Rational numbers (fractions)

3) Computable numbers (often called "exact reals", a term I find
misleading).


Pharo has 1) and 2).


Konrad

Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Esteban A. Maringolo
On Wed, Jun 16, 2021 at 9:13 AM Konrad Hinsen
<[hidden email]> wrote:

>
> On 15/06/2021 16:05, Esteban Maringolo wrote:
> > And in this particular case, it might cause issues, since the "full
> > precision" (whatever that means) must be retained when using such a
> > number to derive another one.
>
> To me, "full precision" means "no precision is ever lost in arithmetic
> operations". If that's what you need, your choices are
>
> 1) Integers
>
> 2) Rational numbers (fractions)

> Pharo has 1) and 2).

Yeap, I'll switch to 2.

What I'm not totally convinced of is whether to store numbers as
integers (knowing beforehand the decimal precision, and converting it
to fractions when read back) or if to store them as scaled decimals
(which internally hold a fraction).

Thanks!
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Richard O'Keefe
In reply to this post by Esteban A. Maringolo
The problem is that while it is possible to read the ANSI Smalltalk standard as requiring ScaledDecimals to be fixed point numbers (which are a kind of exact rational number of the form x * 10**y, x and y integers), in Squeak and Pharo ScaledDecimal numbers are NOT fixed-point numbers.  They are unlimited rational numbers with the scale being used for printing purposes only.  This creates enormous confusion and I really don't see any point in Squeak/Pharo ScaledDecimal existing at all.  There *is* point in having fixed-point decimal numbers as they are a perfect match for SQL data bases and several programming languages.

I've now located a PDF of the USGA Rules of Handicapping.  Wow.  128 pages.
And with all that they couldn't explain the calculations precisely.  Rounding
arrives in more than one place.  For example you might have to average the
best 1-8 of the last (as many as are available up to 20) scores and then round
to one decimal, but it is not stated how ties are to be broken (which can arise
for the best 2, 4, 6, or 8).  It is probably safe to assume rounding out.

Number>>roundOut
  ^self negative
     ifTrue:  [((self * 2 - 1) / 2) ceiling]
     ifFalse: [((self * 2 + 1) / 2) floor]

On Wed, 16 Jun 2021 at 02:06, Esteban Maringolo <[hidden email]> wrote:
Well... fixed point numbers (aka ScaledDecimals) had their issues as well.

And in this particular case, it might cause issues, since the "full
precision" (whatever that means) must be retained when using such a
number to derive another one.

E.g. in my case:
ch := 6.7 + (32.8 - 35). "course handicap"
allowance := 0.85. "85%"
ph := (ch * allowance) roundedHandicap. "playing handicap"

The #roundedHandicap is like #rounded but rounds -0.5 towards -1
instead of toward 0.

My first approach was to use fixed points, but then I hit issues where
some results also produced "wrong" numbers (according to the
reference), also in Pharo some ScaledDecimals compare as different
even when they're printed as the same (which is not correct, IMO).

I just tested using everything as fractions and converting to float
(or ScaledDecimal) at the very last step and it produces the expected
result. I'll review and convert whatever is needed (including database
fields) to work with this. The good thing is that all the involved
numbers only have one decimal as much.

Thanks!

Esteban A. Maringolo

On Tue, Jun 15, 2021 at 3:48 AM Steffen Märcker <[hidden email]> wrote:
>
> Have you considered using fixed-point arithmetic? For example:
> 7.1s2 roundTo: 0.1s2
>
> The rule of thumb I stick to is to use FP only if I know the inaccuracies
> won't bite me. Funny enough, both 7.1 and 0.1 are already not accurately
> representable as floats. (And by coincidence, I prepared exam questions
> about floats for my students yesterday. )
>
> Kind regards,
> Steffen
>
>
> Konrad Hinsen schrieb am Dienstag, 15. Juni 2021 07:02:30 (+02:00):
>
>  > On 15/06/2021 01:03, Esteban Maringolo wrote:
>  > > Sure, but what initiated this thread was a reference to roundTo: 0.1
>  > > which produced a "wrong" output.
>  > >
>  > > (9.1 + (-2.0)) roundTo: 0.1 "=> 7.1000000000000005"
>  > > 7.1 roundTo: 0.1 "=> 7.1000000000000005"
>  > >
>  > > However, at this point I know that Pharo "does the right, raw, thing"
>  > > (at least compared to other mainstream languages), but it still
>  > > produces a surprise effect.
>  >
>  > That's the "floating point surprise" that everyone has at some point, no
> matter the language and runtime system. If that surprise is a problem for
> you, are you sure that floating-point arithmetic is what you really want?
> Maybe your needs are better served with integers and fractions.
>  >
>  >
>  > Konrad.
>  >
>  >
> --
> Gesendet mit Vivaldi Mail. Laden Sie Vivaldi kostenlos von vivaldi.com
> herunter.
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Sven Van Caekenberghe-2
In reply to this post by Esteban A. Maringolo
I am also a bit intrigued by this. Like you said: several other programming languages (I tried a couple of Common Lisp and Scheme implementations) do the same as Pharo, but handheld calculators, normal and scientific, do not.

I think that going for 1/10 fractions/precision is not going to help: you got a division by 113 in your formula [https://en.wikipedia.org/wiki/Handicap_(golf)], this will give smaller fractions.

The problem is not the calculation (modern 64-bit floats as in Pharo are plenty accurate), it is how you handle results. You should just round it correctly and be done with it.

Note that

a roundTo: 0.00000000000001. (1e-14) still gives 4.5

it is only one step further that you hit the limit and get the ugly but correct result.

I assume that most calculators always use a printing precision that is lower than their internal precision, hence they hide this reality.

When computing with money, you would be inclined to put everything in cents (because you cannot divide them further). But once you start computing percentage discounts or taxes, you again get problems. At each of those steps you must make sure that no cents are lost.

> On 16 Jun 2021, at 15:19, Esteban Maringolo <[hidden email]> wrote:
>
> On Wed, Jun 16, 2021 at 9:13 AM Konrad Hinsen
> <[hidden email]> wrote:
>>
>> On 15/06/2021 16:05, Esteban Maringolo wrote:
>>> And in this particular case, it might cause issues, since the "full
>>> precision" (whatever that means) must be retained when using such a
>>> number to derive another one.
>>
>> To me, "full precision" means "no precision is ever lost in arithmetic
>> operations". If that's what you need, your choices are
>>
>> 1) Integers
>>
>> 2) Rational numbers (fractions)
>
>> Pharo has 1) and 2).
>
> Yeap, I'll switch to 2.
>
> What I'm not totally convinced of is whether to store numbers as
> integers (knowing beforehand the decimal precision, and converting it
> to fractions when read back) or if to store them as scaled decimals
> (which internally hold a fraction).
>
> Thanks!
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

khinsen
On 16/06/2021 15:52, Sven Van Caekenberghe wrote:
> I am also a bit intrigued by this. Like you said: several other programming languages (I tried a couple of Common Lisp and Scheme implementations) do the same as Pharo, but handheld calculators, normal and scientific, do not.

Handheld calculators use decimal floats, not binary floats. That doesn't
remove rounding issues, but it makes conversion to and from print
representations loss-free.


Konrad
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Esteban A. Maringolo
In reply to this post by Richard O'Keefe
On Wed, Jun 16, 2021 at 10:49 AM Richard O'Keefe <[hidden email]> wrote:
> The problem is that while it is possible to read the ANSI Smalltalk standard as requiring ScaledDecimals to be fixed point numbers (which are a kind of exact rational number of the form x * 10**y, x and y integers), in Squeak and Pharo ScaledDecimal numbers are NOT fixed-point numbers.  They are unlimited rational numbers with the scale being used for printing purposes only.  This creates enormous confusion and I really don't see any point in Squeak/Pharo ScaledDecimal existing at all.

I only used decimals in other platforms, and only for accounting
purposes, but to me if two scaled decimals print the same but answer
false to #= then something is wrong conceptually. I can identify the
issue, but the reasoning behind how it works, or even more, how to fix
it, is above my level.

> There *is* point in having fixed-point decimal numbers as they are a perfect match for SQL data bases and several programming languages.

That's the only use I gave it.
So if we divided 10.0s1 / 3 ended up with 3.3s1 each, and we had to
calculate the rounding errors (0.1s1 in this case) to make all
balance.

> I've now located a PDF of the USGA Rules of Handicapping.  Wow.  128 pages.

That's just USGA, there is a "World System" but each "region" or local
federation does a variation of the "world system". So Belgium does one
thing, Australia another one, Argentina another one, England decided
to only take one part...  standards, they call it :-)

I Implemented a couple of them.

> And with all that they couldn't explain the calculations precisely.

Try to ask one federation how they calculate the PCC, and they won't
tell you, it's like the coca-cola formula.

>  Rounding
> arrives in more than one place.  For example you might have to average the
> best 1-8 of the last (as many as are available up to 20) scores and then round
> to one decimal, but it is not stated how ties are to be broken (which can arise
> for the best 2, 4, 6, or 8).

In this computation, the tie doesn't matter, because you start with an
_already rounded_ number (called a "differential"), so it doesn't
matter if you choose 11.4 from the 5th or 11.4 from the 19th. The
rounding happens at the very end and determines a new rounded (to one
decimal) number that's going to be used in other formulas (like the
one that re-kicked this thread).

The ties in competitions are solved differently. In real tournaments
by playoff, or in amateur tournaments either by playoff or by a hole
by hole comparison. If that even happens[*], there is the option to
toss a coin to untie it.

[*] In +400 tournaments managed by my software (involving ~15000
scorecards), that never happened.


So to wrap up and bring it back to the topic, a proper ScaledDecimal
implementation would help me storing the numbers as such guaranteeing
no floating point funny thing happens, but since there is no such
thing, I'll rather build the scaled decimals manually (e.g.
ScaledDecimal fromFraction: 67/10) or not use scaled decimals and try
to use the Fractions directly wherever possible.

It's always good to learn new things.
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Esteban A. Maringolo
In reply to this post by Sven Van Caekenberghe-2
On Wed, Jun 16, 2021 at 10:52 AM Sven Van Caekenberghe <[hidden email]> wrote:
>
> I am also a bit intrigued by this. Like you said: several other programming languages (I tried a couple of Common Lisp and Scheme implementations) do the same as Pharo, but handheld calculators, normal and scientific, do not.
>
> I think that going for 1/10 fractions/precision is not going to help: you got a division by 113 in your formula [https://en.wikipedia.org/wiki/Handicap_(golf)], this will give smaller fractions.

And the reasons behind why they chose 113 are still unknown, because
that number is used to get a
"difficulty coefficient", so if the course is rated a 125, then
125/113 will give you roughly a ~1.1 coefficient.
They could have used 100.

> The problem is not the calculation (modern 64-bit floats as in Pharo are plenty accurate), it is how you handle results. You should just round it correctly and be done with it.

> Note that
> a roundTo: 0.00000000000001. (1e-14) still gives 4.5
> it is only one step further that you hit the limit and get the ugly but correct result.

I'm not doing that, and I only store the original number (which has a
fixed decimal) and the "rounded" (to no decimals) number, all
intermediate numbers are re-calculated (maybe cached) but not stored
elsewhere. What I might be doing wrong is to store the original index
as float instead of a Decimal number.

> When computing with money, you would be inclined to put everything in cents (because you cannot divide them further). But once you start computing percentage discounts or taxes, you again get problems. At each of those steps you must make sure that no cents are lost.

And that includes adding a "rounding" item for any difference in all
invoices, orders, etc.

Regards!

Esteban A. Maringolo
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Esteban A. Maringolo
In reply to this post by khinsen
I didn't know there were decimal floats.

Are they used elsewhere?

Esteban A. Maringolo

On Wed, Jun 16, 2021 at 11:20 AM Konrad Hinsen
<[hidden email]> wrote:

>
> On 16/06/2021 15:52, Sven Van Caekenberghe wrote:
> > I am also a bit intrigued by this. Like you said: several other programming languages (I tried a couple of Common Lisp and Scheme implementations) do the same as Pharo, but handheld calculators, normal and scientific, do not.
>
> Handheld calculators use decimal floats, not binary floats. That doesn't
> remove rounding issues, but it makes conversion to and from print
> representations loss-free.
>
>
> Konrad
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Sven Van Caekenberghe-2
In reply to this post by khinsen


> On 16 Jun 2021, at 16:20, Konrad Hinsen <[hidden email]> wrote:
>
> On 16/06/2021 15:52, Sven Van Caekenberghe wrote:
>> I am also a bit intrigued by this. Like you said: several other programming languages (I tried a couple of Common Lisp and Scheme implementations) do the same as Pharo, but handheld calculators, normal and scientific, do not.
>
> Handheld calculators use decimal floats, not binary floats. That doesn't remove rounding issues, but it makes conversion to and from print representations loss-free.
>
>
> Konrad
>

mmm, this is interesting.

It would be possible (and maybe it has already been done) to implement/add such a decimal floating point number to Pharo. It would require reimplementing all operations from scratch (esp. sin/cos/tan log/exp and so on), it would be slow, but interesting.


Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

khinsen
Sven Van Caekenberghe <[hidden email]> writes:

> It would be possible (and maybe it has already been done) to
> implement/add such a decimal floating point number to Pharo. It would
> require reimplementing all operations from scratch (esp. sin/cos/tan
> log/exp and so on), it would be slow, but interesting.

That would be an interesting experiment indeed. Either use one of the
IEEE754 decimal float formats
(https://en.wikipedia.org/wiki/Decimal_floating_point), or Douglas
Crockford's DEC64 proposal (https://www.crockford.com/dec64.html, which
strangely enough does not refer to the older IEEE standard). There are
implementations of both for various other languages that can serve as
inspiration.

Konrad.
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

ducasse
In reply to this post by Richard O'Keefe
Richard

As I said it thousands of times,
        - first we are not good in math
        - second Pharo is what we have and we never said that it is the best system ever
        we just enhanced daily
        - third, I never saw a piece of code from you.
        - if you want to see Pharo improve (which I doubt about) just send us code and tests

I’m sad that you systematically ignore any of my emails on the topic.
You could have an impact but you prefer to hide yourselves in your tower. Good luck with it.

S.
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Richard O'Keefe
In reply to this post by Esteban A. Maringolo
2Decimal floats are (a) part of the IEEE 758-2008 standard for
floating-point arithmetic,
(b) implemented as part of the instruction set in IBM z/Series and
POWER computers,
(c) allowed for in the current C standard, (d) partially supported by GCC
https://gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Decimal-Float.html
and (e) available for most machines via the "reference implementation" in C.
The current COBOL standard basically defines its arithmetic in terms of this.
Java of course has BigDecimal.  While decimal floats became part of IEEE 754 in
2008, they were previously covered by IEEE 854.

Reverting to ScaledDecimal, let us consider the 10.0s1 / 3 example.
There is a huge problem here with no really satisfactory answer.
In my library, it currently answers 3.3s1, exactly equal to 33/10.
In Squeak/Pharo, it answers "I am really 10/3 but print me with one decimal",
so you see 3.3a1 but it *behaves* like 10.3.
In VisualAge Smalltalk you get  3.333333333333333333333333333333
If it were not for the fact that the ANSI Smalltalk standard appears to
require that <scaled decimal> / <integer> yield a <scaled decimal>,
I would prefer to answer 10/3.  Decimals just plain are not closed under
division (although they are closed under addition, subtraction, and
multiplication).  When it comes to scaled decimals, the ANSI Smalltalk
standard is frankly a mess.  Amongst other things, it defers the
semantics of operations on them to the Language-Independent
Arithmetic standard, which explicitly denies having anything to say about them!

I understand why my library does what it does.  I even tried to
publish a paper about it.
I understand why VisualAge Smalltalk does what it does.  (I've read
IBM/360 PrincOps.)
I do NOT understand why Squeak and Pharo do what they do, and if anyone knows
the rationale for their ScaledDecmal please tell me.

On Thu, 17 Jun 2021 at 06:29, Esteban Maringolo <[hidden email]> wrote:

>
> I didn't know there were decimal floats.
>
> Are they used elsewhere?
>
> Esteban A. Maringolo
>
> On Wed, Jun 16, 2021 at 11:20 AM Konrad Hinsen
> <[hidden email]> wrote:
> >
> > On 16/06/2021 15:52, Sven Van Caekenberghe wrote:
> > > I am also a bit intrigued by this. Like you said: several other programming languages (I tried a couple of Common Lisp and Scheme implementations) do the same as Pharo, but handheld calculators, normal and scientific, do not.
> >
> > Handheld calculators use decimal floats, not binary floats. That doesn't
> > remove rounding issues, but it makes conversion to and from print
> > representations loss-free.
> >
> >
> > Konrad
Reply | Threaded
Open this post in threaded view
|

Re: Rounding in Floats

Richard O'Keefe
In reply to this post by ducasse
"first we are not good in math"
  First, I am not criticising YOU.  Pharo's weird ScaledDecimal is
Squeak's weird ScaledDecimal
  and you have not changed it.  Fine.
 "second Pharo is what we have and we never said that it is the best
system ever"
  True, but irrelevant.
"third I never saw a piece of code from you"
  That is not true.  Why, in this thread alone I contributed
#roundOut.  I have provided several
  pieces of code in this mailing list over the years. You can say that
my code is no good, or
  that you don't want it, or that you think there is a better way to
do the (admittedly small)
  things that I have provided, but if you never saw them you must have
shut your eyes.
  In addition, a snapshot of my system is available on the web for
anyone to play with.
  I shall post here when my GitHub repository is populated; I just
have to clear a few more
  things out of the local directory that I don't have the right to distribute.
  (For example, benchmarking my CSV library against NeoCSV meant having to have
  NeoCSV, but it's not mine to distribute.)


On Thu, 17 Jun 2021 at 08:45, ducasse <[hidden email]> wrote:

>
> Richard
>
> As I said it thousands of times,
>         - first we are not good in math
>         - second Pharo is what we have and we never said that it is the best system ever
>         we just enhanced daily
>         - third, I never saw a piece of code from you.
>         - if you want to see Pharo improve (which I doubt about) just send us code and tests
>
> I’m sad that you systematically ignore any of my emails on the topic.
> You could have an impact but you prefer to hide yourselves in your tower. Good luck with it.
>
> S.
Reply | Threaded
Open this post in threaded view
|

R: Re: Rounding in Floats

Lorenzo
In reply to this post by ducasse
You are right!

Lorenzo

-----Messaggio originale-----
Da: ducasse [mailto:[hidden email]]
Inviato: mercoledì 16 giugno 2021 22:44
A: Any question about pharo is welcome <[hidden email]>
Oggetto: [Pharo-users] Re: Rounding in Floats

Richard

As I said it thousands of times,
        - first we are not good in math
        - second Pharo is what we have and we never said that it is the best system ever
        we just enhanced daily
        - third, I never saw a piece of code from you.
        - if you want to see Pharo improve (which I doubt about) just send us code and tests

I’m sad that you systematically ignore any of my emails on the topic.
You could have an impact but you prefer to hide yourselves in your tower. Good luck with it.

S.
12