<div dir="ltr"><div><div><div><div><div>Hmm, after thoughts, denominator length is a bit indirect...<br>For a relative precision, the limit could be something like this:<br><br></div><div>asApproximateFractionPrecision: limit<br>    ...<br></div>    
 [frac = 0 or: [(Fraction numerator: num1 denominator: denom1)) - self) abs <= limit]]<br></div>        whileFalse: [...]<br><br></div>where limit could be specified with relative precision in decimal places:<br><br>asApproximateFractionRelativeDecimalPlaces: decimalPlaces<br></div>    ^self 
asApproximateFractionPrecision: 
(self timesTwoPower: self exponent - (decimalPlaces * 10 ln / 2 ln) rounded)<br><br></div>and we could use as default 0.0 or 
self ulp / 2.
<div><br>asApproximateFraction<br></div>    ^self 
asApproximateFractionPrecision: self ulp / 2 

<br><div><div><div><div><span class="gmail-im"></span>

<br><br></div></div></div></div></div><div class="gmail_extra"><br><div class="gmail_quote">2018-04-26 9:54 GMT+02:00 Nicolas Cellier <span dir="ltr"><<a href="mailto:nicolas.cellier.aka.nice@gmail.com" target="_blank">nicolas.cellier.aka.nice@gmail.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote"><span class="">2018-04-26 9:53 GMT+02:00 Nicolas Cellier <span dir="ltr"><<a href="mailto:nicolas.cellier.aka.nice@gmail.com" target="_blank">nicolas.cellier.aka.nice@<wbr>gmail.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div>Also note that in base 2, we know the unit of least precision<br></div>So we could stop the loop when precision reaches half ulp, or<br><br>bitLimit := 1 - self ulp exponent.<br></div>[frac = 0 and: [denom1 highBit > bitLimit or: [...]] whileFalse: [...]<br></div><div class="m_-3921360699562854954HOEnZb"><div class="m_-3921360699562854954h5"><div class="gmail_extra"><br></div></div></div></blockquote><div><br></div></span><div>Err, I forgot to tell the aim: get a relative precision<br><br> <br></div><div><div class="h5"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="m_-3921360699562854954HOEnZb"><div class="m_-3921360699562854954h5"><div class="gmail_extra"><div class="gmail_quote">2018-04-26 9:45 GMT+02:00 Nicolas Cellier <span dir="ltr"><<a href="mailto:nicolas.cellier.aka.nice@gmail.com" target="_blank">nicolas.cellier.aka.nice@gmai<wbr>l.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div><div><div>Here is the version that i use in VW (SYSBUG-FloatConversion in cincom public store)<br><br>asApproximateFraction<br>    "Answer a Rational number--Integer or Fraction--representing the receiver.<span><br>    This conversion uses the continued fraction method to approximate <br>    a floating point number."<br><br></span>    | num1 denom1 num2 denom2 int frac newD temp limit |<br>    num1 := self asFraction.    "use exact arithmetic"<br>    frac := num1 fractionPart.        "The fractional part of self"<br>    int := num1 truncated.        "The integer part of self"<br>    num1 := int.    "The first of two alternating numerators"<span><br>    denom1 := 1.        "The first of two alternating denominators"<br>    num2 := 1.        "The second numerator"<br>    denom2 := 0.        "The second denominator--will update"<br></span>    limit := self class precision + 7 // 8 + 1.<br>    [frac = 0 or: [denom1 digitLength > limit or: [(self coerce: (Fraction numerator: num1 denominator: denom1)) = self]]]<span><br>        whileFalse: <br>            ["repeat while the fractional part is not zero"<br></span>            newD := frac reciprocal.            "Take reciprocal of the fractional part"<br>            int := newD truncated.        "get the integer part of this"<span><br>            frac := newD fractionPart.    "and save the fractional part for next time"<br>            temp := num2.                "Get old numerator and save it"<br>            num2 := num1.                "Set second numerator to first"<br>            num1 := num1 * int + temp.    "Update first numerator"<br>            temp := denom2.                "Get old denominator and save it"<br>            denom2 := denom1.            "Set second denominator to first"<br></span>            denom1 := int * denom1 + temp.        "Update first denominator"].<span><br>    "If fractional part is zero, return the first ratio"<br>    denom1 = 1<br></span>        ifTrue: ["Am i really an Integer?"<br>                ^num1"Yes, return Integer result"]<span><br>        ifFalse: ["Otherwise return Fraction result"<br>                ^Fraction numerator: num1 denominator: denom1]<br><br></span></div>Note that I use self asFraction (asTrueFraction) and then perform all operations in Integer arithmetic.<br></div>#digitLength is the number of bytes like in Squeak.<br><br>For user-defined controlled precision like aimed by Eliot, we could use #decimalDigitLength and seed it by default with something like #decimalPrecision in Squeak.<br>
#decimalPrecision does not exist yet

but could be (self class precision * 2 ln / 10 ln) rounded - or simply 15 or 16.<br></div><div>Note that it's an absolute precision, not a relative precision which is used.<br></div><div><br></div>Also note that we could rewrite the iteration like this:<br></div>    num1 := num2 + ((num2 := num1) * int).<br>    den1 := den2 + ((den2 := den1) * int).

<br></div><div class="m_-3921360699562854954m_-5895464051601559844HOEnZb"><div class="m_-3921360699562854954m_-5895464051601559844h5"><div class="gmail_extra"><br><div class="gmail_quote">2018-04-26 4:48 GMT+02:00  <span dir="ltr"><<a href="mailto:commits@source.squeak.org" target="_blank">commits@source.squeak.org</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">David T. Lewis uploaded a new version of Kernel to project The Inbox:<br>
<a href="http://source.squeak.org/inbox/Kernel-dtl.1166.mcz" rel="noreferrer" target="_blank">http://source.squeak.org/inbox<wbr>/Kernel-dtl.1166.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: Kernel-dtl.1166<br>
Author: dtl<br>
Time: 25 April 2018, 10:48:38.572237 pm<br>
UUID: 43d80609-fbc5-40a1-9d49-37d2c4<wbr>f0b306<br>
Ancestors: Kernel-dtl.1165<br>
<br>
In calculation of asApproximateFraction, the digit count for floatPrecision should be<br>
an integer, so do not convert it with asFlloat.<br>
<br>
Allows the following to be resolved without infinite loop on comparison to Float infinity:<br>
        ((FloatArray new: 1) at: 1 put: 1 / 3; at: 1) asApproximateFractionFloatPrec<wbr>ision: 10000<br>
<br>
=============== Diff against Kernel-dtl.1165 ===============<br>
<br>
Item was changed:<br>
  ----- Method: Float>>asApproximateFractionAt<wbr>Order:floatPrecision: (in category 'converting') -----<br>
  asApproximateFractionAtOrder: maxOrder floatPrecision: decimalPlaces<br>
        "Answer a Fraction approximating the receiver. This conversion uses the <br>
        continued fraction method to approximate a floating point number. If maxOrder<br>
        is zero, use maximum order. Assume that I am a float with decimalPlaces of<br>
        meaningful accuracy, regardless of the precision of my internal representation."<br>
<br>
        | num1 denom1 num2 denom2 int frac newD temp order floatPrecision |<br>
        num1 := self asInteger. "The first of two alternating numerators"<br>
        denom1 := 1.            "The first of two alternating denominators"<br>
        num2 := 1.              "The second numerator"<br>
        denom2 := 0.            "The second denominator--will update"<br>
        int := num1.            "The integer part of self"<br>
        frac := self fractionPart.              "The fractional part of self"<br>
        order := maxOrder = 0 ifTrue: [-1] ifFalse: [maxOrder].<br>
+       floatPrecision := 10 raisedTo: decimalPlaces.<br>
-       floatPrecision := (10 raisedTo: decimalPlaces) asFloat.<br>
        [frac = 0 or: [order = 0] ]<br>
                whileFalse: <br>
                        ["repeat while the fractional part is not zero and max order is not reached"<br>
                        order := order - 1.<br>
                        newD := 1.0 / frac.                     "Take reciprocal of the fractional part"<br>
                        int := newD asInteger.          "get the integer part of this"<br>
                        frac := newD fractionPart.      "and save the fractional part for next time"<br>
                        temp := num2.                           "Get old numerator and save it"<br>
                        num2 := num1.                           "Set second numerator to first"<br>
                        num1 := num1 * int + temp.      "Update first numerator"<br>
                        temp := denom2.                         "Get old denominator and save it"<br>
                        denom2 := denom1.                       "Set second denominator to first"<br>
                        denom1 := int * denom1 + temp.          "Update first denominator"<br>
                        floatPrecision < denom1<br>
                                ifTrue: <br>
                                        ["Is ratio past float precision?  If so, pick which <br>
                                        of the two ratios to use"<br>
                                        num2 = 0.0 <br>
                                                ifTrue: ["Is second denominator 0?"<br>
                                                                ^ Fraction numerator: num1 denominator: denom1].<br>
                                        ^ Fraction numerator: num2 denominator: denom2]].<br>
        "If fractional part is zero, return the first ratio"<br>
        denom1 = 1<br>
                ifTrue: ["Am I really an Integer?"<br>
                                ^ num1 "Yes, return Integer result"]<br>
                ifFalse: ["Otherwise return Fraction result"<br>
                                ^ Fraction numerator: num1 denominator: denom1]!<br>
<br>
<br>
</blockquote></div><br></div>
</div></div></blockquote></div><br></div>
</div></div></blockquote></div></div></div><br></div></div>
</blockquote></div><br></div>