The Inbox: Kernel-dtl.1166.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

The Inbox: Kernel-dtl.1166.mcz

commits-2
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]!


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-dtl.1166.mcz

Nicolas Cellier
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 <[hidden email]>:
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]!





Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-dtl.1166.mcz

Nicolas Cellier
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: [...]

2018-04-26 9:45 GMT+02:00 Nicolas Cellier <[hidden email]>:
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 <[hidden email]>:
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]!






Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-dtl.1166.mcz

Nicolas Cellier


2018-04-26 9:53 GMT+02:00 Nicolas Cellier <[hidden email]>:
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 <[hidden email]>:
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 <[hidden email]>:
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]!







Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-dtl.1166.mcz

Nicolas Cellier
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 <[hidden email]>:


2018-04-26 9:53 GMT+02:00 Nicolas Cellier <[hidden email]>:
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 <[hidden email]>:
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 <[hidden email]>:
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]!