The behaviour of ScaledDecimal in Squeak and Pharo is
- different from what the ANSI Smalltalk standard says
- different from what other Smalltalks do (not that they agree)
- completely different from decimal arithmetic in COBOL, PL/I, or SQL
- seriously confusing.

What you EXPECT is an exact rational number that is equal to an
integer divided by a power of 10, so that the printed
representation faithfully represents the value with no error.
What you GET in Pharo is a rational number that PRINTS with
a given number of decimal places but that is all.

Thus we expect that
  x := 10/3 asScaledDecimal: 2.
  x asString ==> '3.33s2'
  y := x+x+x.
  y asString ==> '9.99s2'
But no, y asString => '10.00s2'.

Here's what the comment in the VW class FixedPoint says:
"There are two possible ways to express FixedPoint numbers.  One is as a
scaled Integer, but the problem here is that you can lose precision during
intermediate calculations.  For example, a property that seems useful is
that the calculation (1.000 / 7 * 7) should give you back the number 1.000.

For this reason, we have adopted an alternative representation, which is a
slight variation on Fraction.  The number is expressed as the ratio of a
numerator to a denominator, with the addition of a precision that is used
for printing and for rounding.  The number is not usually rounded off to
its scale, but if an intermediate result must be rounded to its scale
before being used, the messages #roundedToScale and #truncatedToScale may
be used."

The last sentence in the first paragraph is one I cannot agree with.
If I want a calculation where (1/7 * 7) gives me back 1, then I use
exact Fractions. If I am using ScaledDecimal, it is because I *want*
fixed point decimal numbers, with the properties appropriate to fixed
point decimal numbers.  Wanting something that I could use to talk to
databases like MariaDB I found that I had to write my own FixedPoint
class, only to find that VW called ScaledDecimal FixedPoint.  *Sigh*.

Unless and until the current ScaledDecimal is ripped out and buried
in an unmarked grave at a crossroad with a stake through its heart,
you may wish to add VW-compatibility methods

roundedToScale
   |t|
   t := 10 raisedToInteger: scale.
   ^(numerator * t / denominator) rounded / t asScaledDecimal: scale

truncatedToScale
   |t|
   t := 10 raisedToInteger: scale.
   ^(numerator * t / denominator) truncated / t asScaledDecimal: scale

and then use

  aScaledDecimal roundedToScale = anotherOne roundedToScale

Note that the ANSI standard, which was agreed to by a whole bunch of
Smalltalk experts, says "A <scaledDecimal> converted to a <Fraction>
will be a fraction having the same numeric value but having an integer
numerator and a denominator which is ten raised to
the power of the <scaledDecimal>’s scale factor."
Try "(10.00s2 / 3) asFraction" in your Smalltalk, and if the
result has a denominator of 3, SOMEONE stuffed up.




On Tue, 1 Sep 2020 at 16:08, Esteban Maringolo <emaring...@gmail.com> wrote:

> Hi,
>
> I was doing some basic calculations based on a formula, and I wanted
> to convert the result to a scaled decimal in order to avoid having
> these "loose" decimals in 10th position or something similar.
>
> So I did the following:
> 82 - (2 * 35.9) - (0 / 2) * (113/121) asScaledDecimal: 1 -> 9.5s1
>
> But When I do this in a test:
> (82 - (2 * 35.9) - (0 / 2) * (113/121) asScaledDecimal: 1) = 9.5s1
>
> It fails because the comparison returns false.
>
> I guess this is the proper behavior, but I'd expected that the
> conversion from a Float to a scaled decimal would have eliminated the
> extra precision from the formula.
>
> I can do a roundTo: 0.1 before converting, but I'd like to know what
> would be the proper way of dealing with this.
>
> Thanks!
>
> Esteban A. Maringolo
>
>

Reply via email to