[squeak-dev] asApproximateFraction[AtOrder:]

David T. Lewis lewis at mail.msen.com
Tue Apr 24 03:06:02 UTC 2018


Hi Eliot,

I think that the confusing behavior is is related to the interpretation of single
precision floats in the VM when represented as double precision Float in Squeak.

How about implementing #asApproximateFractionFloatPrecision: to specify the
assumed accuracy of the float when derived from single precision, but represented
in the image as a double precision Squeak float?

Thus:

  (FloatArray new: 1) at: 1 put: 1/3; at: 1 ==> 0.3333333432674408 "single precision float cast from FloatArray to Squeak double precision"

  0.3333333432674408 asApproximateFraction ==> (11184811/33554432) "bad, original default implementation assumes float double precision"

  0.3333333432674408 asApproximateFractionFloatPrecision: 5 ==> (1/3) "better, limit precision when cast from single precision to double"

An implementation is in the inbox in Kernel-dtl.1165.

Note, orignal author for these methods has initials 'st' is, which according to
SqueakMap is Samuel Tardieu (sam at rfc1149.net, home page https://rfc1149.net/sam.html).
Even for those of us not who may not be interested in numerical methods, I must
highly recommend his references to rfc1149.

Dave


On Mon, Apr 23, 2018 at 01:23:03PM -0700, Eliot Miranda wrote:
> Hi All,
> 
>     asApproximateFraction isn't that useful.  It is based on
> asApproximateFractionAtOrder:, which gives you the best fraction it can
> find up to order.  e.g.
> 
> testContinuedFractions
> self assert: (Float pi asApproximateFractionAtOrder: 1) = (22/7).
> self assert: (Float pi asApproximateFractionAtOrder: 3) = (355/113)
> Here's 32-bit Float 1/3:
> 
> ((FloatArray new: 1) at: 1 put: 1/3; at: 1) 0.3333333432674408
> 
> ((FloatArray new: 1) at: 1 put: 1/3; at: 1) asApproximateFraction
> (11184811/33554432)
> 
> That's not what I expected :-).  The problem is that
> asApproximateFractionAtOrder: is great if you know the number you're
> dealing with, but if you don't then it'll give you too much information.
> 
> [This value comes up in the vm parameters system report page, where 1/3 is
> the ratio of growth to heap size above which a full GC is performed, i.e.
> by default every time a scavenge causes the heap grows by 1/3 from the last
> time a full GC was performed, the system will do a full GC.  It would be
> great to report this as 1/3, not 0.33333298563957214, which is what's
> emerging from the C code in the VM].
> 
> Let's get a feeling for orders; they're effectively negative powers of 10:
> 
> (1 to: 20) collect: [:order| | f |
> { order. (f := Float pi asApproximateFractionAtOrder: order). f asFloat. (f
> asFloat - Float pi) abs}]
> {{1 . (22/7) . 3.142857142857143 . 0.0012644892673496777}.
>  {2 . (333/106) . 3.141509433962264 . 8.32196275291075e-5}.
>  {3 . (355/113) . 3.1415929203539825 . 2.667641894049666e-7}.
>  {4 . (103993/33102) . 3.1415926530119025 . 5.778906242426274e-10}.
>  {5 . (104348/33215) . 3.141592653921421 . 3.3162805834763276e-10}.
>  {6 . (208341/66317) . 3.1415926534674368 . 1.2235634727630895e-10}.
>  {7 . (312689/99532) . 3.1415926536189365 . 2.914335439641036e-11}.
>  {8 . (833719/265381) . 3.141592653581078 . 8.715250743307479e-12}.
>  {9 . (1146408/364913) . 3.141592653591404 . 1.6107115641261771e-12}.
>  {10 . (4272943/1360120) . 3.141592653589389 . 4.04121180963557e-13}.
>  {11 . (5419351/1725033) . 3.1415926535898153 . 2.220446049250313e-14}.
>  {12 . (80143857/25510582) . 3.1415926535897927 . 4.440892098500626e-16}.
>  {13 . (245850922/78256779) . 3.141592653589793 . 0.0}.
>  {14 . (817696623/260280919) . 3.141592653589793 . 0.0}.
>  {15 . (19052873251/6064717916) . 3.141592653589793 . 0.0}.
>  {16 . (19870569874/6324998835) . 3.141592653589793 . 0.0}.
>  {17 . (19870569874/6324998835) . 3.141592653589793 . 0.0}.
>  {18 . (19870569874/6324998835) . 3.141592653589793 . 0.0}.
>  {19 . (19870569874/6324998835) . 3.141592653589793 . 0.0}.
>  {20 . (19870569874/6324998835) . 3.141592653589793 . 0.0}}
> 
> 
> More useful would be something like:
> 
> asApproximateFractionToEpsilon: epsilon
> "Answer a Fraction approximating the receiver. This conversion uses the
> continued fraction method to approximate a floating point number."
> 
> 1 to: 12 do:
> [:order| | fraction |
> fraction := self asApproximateFractionAtOrder: order.
> (fraction - self) abs <= epsilon ifTrue:
> [^fraction]].
> ^ self asApproximateFractionAtOrder: 0
> 
> and then instead of
> 
> asApproximateFraction
> "Answer a Fraction approximating the receiver. This conversion uses the
> continued fraction method to approximate a floating point number."
> 
> ^ self asApproximateFractionAtOrder: 0
> 
> one could have
> 
> asApproximateFraction
> "Answer a Fraction approximating the receiver. This conversion uses the
> continued fraction method to approximate a floating point number."
> 
> ^self asApproximateFractionToEpsilon: 1e-6
> 
> And hence
> 
> { 0.0. 0.333. 0.5. 1.0. Float pi . ((FloatArray new: 1) at: 1 put: 1/3; at:
> 1) } collect:
>     [:n| n asApproximateFractionToEpsilon: 1e-6]
> {0 . (333/1000) . (1/2) . 1 . (355/113) . (1/3)}
> 
> Votes for or against changing asApproximateFraction to use
> asApproximateFractionToEpsilon:? (asApproximateFraction has no senders into
> base image)
> 
> Suggestions for a selector that would use self
> asApproximateFractionToEpsilon: 1e-6 (less clumsy than e.g.
> asUsefulApproximateFraction).
> 
> _,,,^..^,,,_
> best, Eliot

> 



More information about the Squeak-dev mailing list