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

Nicolas Cellier nicolas.cellier.aka.nice at gmail.com
Thu Apr 26 08:23:21 UTC 2018


Hmm, after thoughts, denominator length is a bit indirect...
For a relative precision, the limit could be something like this:

asApproximateFractionPrecision: limit
    ...
    [frac = 0 or: [(Fraction numerator: num1 denominator: denom1)) - self)
abs <= limit]]
        whileFalse: [...]

where limit could be specified with relative precision in decimal places:

asApproximateFractionRelativeDecimalPlaces: decimalPlaces
    ^self asApproximateFractionPrecision: (self timesTwoPower: self
exponent - (decimalPlaces * 10 ln / 2 ln) rounded)

and we could use as default 0.0 or self ulp / 2.

asApproximateFraction
    ^self asApproximateFractionPrecision: self ulp / 2



2018-04-26 9:54 GMT+02:00 Nicolas Cellier <
nicolas.cellier.aka.nice at gmail.com>:

>
>
> 2018-04-26 9:53 GMT+02:00 Nicolas Cellier <nicolas.cellier.aka.nice@
> gmail.com>:
>
>> Also note that in base 2, we know the unit of least precision
>> So we could stop the loop when precision reaches half ulp, or
>>
>> bitLimit := 1 - self ulp exponent.
>> [frac = 0 and: [denom1 highBit > bitLimit or: [...]] whileFalse: [...]
>>
>>
> Err, I forgot to tell the aim: get a relative precision
>
>
>
>> 2018-04-26 9:45 GMT+02:00 Nicolas Cellier <nicolas.cellier.aka.nice at gmai
>> l.com>:
>>
>>> 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/d3a13be8/attachment-0001.html>


More information about the Squeak-dev mailing list