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