[squeak-dev] The Inbox: Kernel-dtl.1166.mcz

Nicolas Cellier nicolas.cellier.aka.nice at gmail.com
Thu Apr 26 07:45:48 UTC 2018


Here is the version that i use in VW (SYSBUG-FloatConversion in cincom
public store)

asApproximateFraction
    "Answer a Rational number--Integer or Fraction--representing the
receiver.
    This conversion uses the continued fraction method to approximate
    a floating point number."

    | num1 denom1 num2 denom2 int frac newD temp limit |
    num1 := self asFraction.    "use exact arithmetic"
    frac := num1 fractionPart.        "The fractional part of self"
    int := num1 truncated.        "The integer part of self"
    num1 := int.    "The first of two alternating numerators"
    denom1 := 1.        "The first of two alternating denominators"
    num2 := 1.        "The second numerator"
    denom2 := 0.        "The second denominator--will update"
    limit := self class precision + 7 // 8 + 1.
    [frac = 0 or: [denom1 digitLength > limit or: [(self coerce: (Fraction
numerator: num1 denominator: denom1)) = self]]]
        whileFalse:
            ["repeat while the fractional part is not zero"
            newD := frac reciprocal.            "Take reciprocal of the
fractional part"
            int := newD truncated.        "get the integer part of this"
            frac := newD fractionPart.    "and save the fractional part for
next time"
            temp := num2.                "Get old numerator and save it"
            num2 := num1.                "Set second numerator to first"
            num1 := num1 * int + temp.    "Update first numerator"
            temp := denom2.                "Get old denominator and save it"
            denom2 := denom1.            "Set second denominator to first"
            denom1 := int * denom1 + temp.        "Update first
denominator"].
    "If fractional part is zero, return the first ratio"
    denom1 = 1
        ifTrue: ["Am i really an Integer?"
                ^num1"Yes, return Integer result"]
        ifFalse: ["Otherwise return Fraction result"
                ^Fraction numerator: num1 denominator: denom1]

Note that I use self asFraction (asTrueFraction) and then perform all
operations in Integer arithmetic.
#digitLength is the number of bytes like in Squeak.

For user-defined controlled precision like aimed by Eliot, we could use
#decimalDigitLength and seed it by default with something like
#decimalPrecision in Squeak.
#decimalPrecision does not exist yet but could be (self class precision * 2
ln / 10 ln) rounded - or simply 15 or 16.
Note that it's an absolute precision, not a relative precision which is
used.

Also note that we could rewrite the iteration like this:
    num1 := num2 + ((num2 := num1) * int).
    den1 := den2 + ((den2 := den1) * int).

2018-04-26 4:48 GMT+02:00 <commits at source.squeak.org>:

> David T. Lewis uploaded a new version of Kernel to project The Inbox:
> http://source.squeak.org/inbox/Kernel-dtl.1166.mcz
>
> ==================== Summary ====================
>
> Name: Kernel-dtl.1166
> Author: dtl
> Time: 25 April 2018, 10:48:38.572237 pm
> UUID: 43d80609-fbc5-40a1-9d49-37d2c4f0b306
> Ancestors: Kernel-dtl.1165
>
> In calculation of asApproximateFraction, the digit count for
> floatPrecision should be
> an integer, so do not convert it with asFlloat.
>
> Allows the following to be resolved without infinite loop on comparison to
> Float infinity:
>         ((FloatArray new: 1) at: 1 put: 1 / 3; at: 1)
> asApproximateFractionFloatPrecision: 10000
>
> =============== Diff against Kernel-dtl.1165 ===============
>
> Item was changed:
>   ----- Method: Float>>asApproximateFractionAtOrder:floatPrecision: (in
> category 'converting') -----
>   asApproximateFractionAtOrder: maxOrder floatPrecision: decimalPlaces
>         "Answer a Fraction approximating the receiver. This conversion
> uses the
>         continued fraction method to approximate a floating point number.
> If maxOrder
>         is zero, use maximum order. Assume that I am a float with
> decimalPlaces of
>         meaningful accuracy, regardless of the precision of my internal
> representation."
>
>         | num1 denom1 num2 denom2 int frac newD temp order floatPrecision |
>         num1 := self asInteger. "The first of two alternating numerators"
>         denom1 := 1.            "The first of two alternating denominators"
>         num2 := 1.              "The second numerator"
>         denom2 := 0.            "The second denominator--will update"
>         int := num1.            "The integer part of self"
>         frac := self fractionPart.              "The fractional part of
> self"
>         order := maxOrder = 0 ifTrue: [-1] ifFalse: [maxOrder].
> +       floatPrecision := 10 raisedTo: decimalPlaces.
> -       floatPrecision := (10 raisedTo: decimalPlaces) asFloat.
>         [frac = 0 or: [order = 0] ]
>                 whileFalse:
>                         ["repeat while the fractional part is not zero and
> max order is not reached"
>                         order := order - 1.
>                         newD := 1.0 / frac.                     "Take
> reciprocal of the fractional part"
>                         int := newD asInteger.          "get the integer
> part of this"
>                         frac := newD fractionPart.      "and save the
> fractional part for next time"
>                         temp := num2.                           "Get old
> numerator and save it"
>                         num2 := num1.                           "Set
> second numerator to first"
>                         num1 := num1 * int + temp.      "Update first
> numerator"
>                         temp := denom2.                         "Get old
> denominator and save it"
>                         denom2 := denom1.                       "Set
> second denominator to first"
>                         denom1 := int * denom1 + temp.          "Update
> first denominator"
>                         floatPrecision < denom1
>                                 ifTrue:
>                                         ["Is ratio past float precision?
> If so, pick which
>                                         of the two ratios to use"
>                                         num2 = 0.0
>                                                 ifTrue: ["Is second
> denominator 0?"
>                                                                 ^ Fraction
> numerator: num1 denominator: denom1].
>                                         ^ Fraction numerator: num2
> denominator: denom2]].
>         "If fractional part is zero, return the first ratio"
>         denom1 = 1
>                 ifTrue: ["Am I really an Integer?"
>                                 ^ num1 "Yes, return Integer result"]
>                 ifFalse: ["Otherwise return Fraction result"
>                                 ^ Fraction numerator: num1 denominator:
> denom1]!
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20180426/8922f754/attachment.html>


More information about the Squeak-dev mailing list