[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
|