pharo-users@lists.pharo.org

Any question about pharo is welcome

View all threads

Rounding in Floats

SM
Steffen Märcker
Tue, Jun 15, 2021 6:49 AM

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.

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.
RO
Richard O'Keefe
Tue, Jun 15, 2021 7:12 AM

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 emaringolo@gmail.com wrote:

On Mon, Jun 14, 2021 at 10:37 PM Richard O'Keefe raoknz@gmail.com 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 emaringolo@gmail.com 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 emaringolo@gmail.com wrote:

On Tue, Sep 8, 2020 at 12:16 AM Richard O'Keefe raoknz@gmail.com 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

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 <emaringolo@gmail.com> wrote: > > On Mon, Jun 14, 2021 at 10:37 PM Richard O'Keefe <raoknz@gmail.com> 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 <emaringolo@gmail.com> 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 <emaringolo@gmail.com> wrote: > > > > > > > > On Tue, Sep 8, 2020 at 12:16 AM Richard O'Keefe <raoknz@gmail.com> 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
EM
Esteban Maringolo
Tue, Jun 15, 2021 2:05 PM

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 merkste@web.de 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.

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 <merkste@web.de> 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.
EM
Esteban Maringolo
Tue, Jun 15, 2021 2:15 PM

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 raoknz@gmail.com wrote:

On Tue, 15 Jun 2021 at 14:24, Esteban Maringolo emaringolo@gmail.com wrote:

On Mon, Jun 14, 2021 at 10:37 PM Richard O'Keefe raoknz@gmail.com 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 emaringolo@gmail.com 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 emaringolo@gmail.com wrote:

On Tue, Sep 8, 2020 at 12:16 AM Richard O'Keefe raoknz@gmail.com 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

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 <raoknz@gmail.com> wrote: > > > > On Tue, 15 Jun 2021 at 14:24, Esteban Maringolo <emaringolo@gmail.com> wrote: > > > > On Mon, Jun 14, 2021 at 10:37 PM Richard O'Keefe <raoknz@gmail.com> 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 <emaringolo@gmail.com> 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 <emaringolo@gmail.com> wrote: > > > > > > > > > > On Tue, Sep 8, 2020 at 12:16 AM Richard O'Keefe <raoknz@gmail.com> 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
EM
Esteban Maringolo
Tue, Jun 15, 2021 2:19 PM

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 sven@stfx.eu 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 sven@stfx.eu wrote:

On 14 Jun 2021, at 22:44, Esteban Maringolo emaringolo@gmail.com 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 emaringolo@gmail.com wrote:

On Tue, Sep 8, 2020 at 12:16 AM Richard O'Keefe raoknz@gmail.com 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

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 <sven@stfx.eu> 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 <sven@stfx.eu> wrote: > > > > > > > >> On 14 Jun 2021, at 22:44, Esteban Maringolo <emaringolo@gmail.com> 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 <emaringolo@gmail.com> wrote: > >>> > >>> On Tue, Sep 8, 2020 at 12:16 AM Richard O'Keefe <raoknz@gmail.com> 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
SV
Sven Van Caekenberghe
Tue, Jun 15, 2021 2:52 PM

On 15 Jun 2021, at 16:19, Esteban Maringolo emaringolo@gmail.com 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 sven@stfx.eu 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 sven@stfx.eu wrote:

On 14 Jun 2021, at 22:44, Esteban Maringolo emaringolo@gmail.com 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 emaringolo@gmail.com wrote:

On Tue, Sep 8, 2020 at 12:16 AM Richard O'Keefe raoknz@gmail.com 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

> On 15 Jun 2021, at 16:19, Esteban Maringolo <emaringolo@gmail.com> 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 <sven@stfx.eu> 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 <sven@stfx.eu> wrote: >>> >>> >>> >>>> On 14 Jun 2021, at 22:44, Esteban Maringolo <emaringolo@gmail.com> 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 <emaringolo@gmail.com> wrote: >>>>> >>>>> On Tue, Sep 8, 2020 at 12:16 AM Richard O'Keefe <raoknz@gmail.com> 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
KH
Konrad Hinsen
Wed, Jun 16, 2021 12:13 PM

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

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
EM
Esteban Maringolo
Wed, Jun 16, 2021 1:19 PM

On Wed, Jun 16, 2021 at 9:13 AM Konrad Hinsen
konrad.hinsen@fastmail.net 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!

On Wed, Jun 16, 2021 at 9:13 AM Konrad Hinsen <konrad.hinsen@fastmail.net> 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!
RO
Richard O'Keefe
Wed, Jun 16, 2021 1:49 PM

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 emaringolo@gmail.com
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 merkste@web.de 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.

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 <emaringolo@gmail.com> 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 <merkste@web.de> 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. >
SV
Sven Van Caekenberghe
Wed, Jun 16, 2021 1:52 PM

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 emaringolo@gmail.com wrote:

On Wed, Jun 16, 2021 at 9:13 AM Konrad Hinsen
konrad.hinsen@fastmail.net 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!

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 <emaringolo@gmail.com> wrote: > > On Wed, Jun 16, 2021 at 9:13 AM Konrad Hinsen > <konrad.hinsen@fastmail.net> 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!