LimitedPrecisionReal.st
changeset 5883 658ff91cef9e
parent 5868 d497030b1df7
child 5954 d706efecb134
--- a/LimitedPrecisionReal.st	Mon Jun 25 09:03:35 2001 +0200
+++ b/LimitedPrecisionReal.st	Tue Jun 26 16:03:35 2001 +0200
@@ -184,46 +184,59 @@
 !
 
 asFraction
-    "return a corresponding fraction or integer
-     (fractions with 1 as denominator are not allowed/useful)
-     - notice, that 'aFract asFloat asFraction' does not always return
-     a good fraction ... due to rounding errors when converting to float"
+    "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|
 
-    |fract digits power num denom eI|
-
-    "we (indirectly) use printf which knows the precision of floats"
-
-    fract := self fractionPart.
-    fract = 0.0 ifTrue:[
-        ^ self asInteger.
+    limit := (self class unity * 10) raisedTo:self defaultNumberOfDigits.
+    num1 := self truncated. 
+    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.             
+    [frac = 0] whileFalse:[                
+        newD := 1.0 / frac.                   
+        int := newD truncated.        
+        frac := newD fractionPart.      "save the fractional part for next time"
+        temp := num2.                   "old numerator and save it"
+        num2 := num1.                         
+        num1 := num1 * int + temp.      "Update first numerator"
+        temp := denom2.                 "old denominator and save it"
+        denom2 := denom1.                    
+        denom1 := int * denom1 + temp.  "Update first denominator"
+        limit < 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
+        ]
     ].
 
-    "/ use printf, so we dont need to know anything
-    "/ about float representation here.
-
-    digits := (fract printfPrintString:'%.16f').
-    eI := digits size.
-    [(digits at:eI) == $0] whileTrue:[
-        eI := eI - 1
+    "If fractional part is zero, return the first ratio"
+    denom1 = 1 ifTrue:[
+        "Am i really an Integer?"
+        ^ num1 "Yes, return Integer result"
     ].
-
-    power := eI - 3 + 1.
-    num := (self - fract) asInteger.
-    denom := (10 raisedToInteger:power).
-    num := num * denom.
-    num := num + (fract * denom) asInteger.
-
-    ^ Fraction numerator:num denominator:denom
+    "Otherwise return Fraction result"
+    ^ Fraction numerator:num1 denominator:denom1
 
     "
-     0.3 asFraction 
+     1.1 asFraction      
+     1.2 asFraction      
+     0.3 asFraction   
      0.5 asFraction 
      (1/5) asFloat asFraction
-     (1/8) asFloat asFraction
-     (1/13) asFloat asFraction
-     3.14159 asFraction 
-     1.3 asFraction
-     1.0 asFraction
+     (1/8) asFloat asFraction  
+     (1/13) asFloat asFraction 
+     3.14159 asFraction        
+     3.14159 asFraction asFloat       
+     1.3 asFraction  
+     1.0 asFraction  
     "
 
     "Modified: / 25.10.1997 / 16:41:19 / cg"
@@ -267,6 +280,27 @@
     "Modified: 12.2.1997 / 16:45:07 / cg"
 !
 
+asRational
+    "Answer a Rational number--Integer or Fraction--representing the receiver.
+     Same as asFraction fro st-80 compatibility."
+
+    ^ self asFraction
+
+    "
+     1.1 asRational      
+     1.2 asRational      
+     0.3 asRational   
+     0.5 asRational 
+     (1/5) asFloat asRational
+     (1/8) asFloat asRational  
+     (1/13) asFloat asRational 
+     3.14159 asRational        
+     3.14159 asRational asFloat       
+     1.3 asRational  
+     1.0 asRational  
+    "
+!
+
 coerce:aNumber
     "return aNumber converted into receivers type"
 
@@ -442,13 +476,16 @@
 
 !LimitedPrecisionReal methodsFor:'queries'!
 
+defaultNumberOfDigits
+    self subclassResponsibility
+!
+
 size
    "redefined since reals are kludgy (ByteArry)"
 
    ^ 0
 ! !
 
-
 !LimitedPrecisionReal methodsFor:'testing'!
 
 isFinite
@@ -521,5 +558,5 @@
 !LimitedPrecisionReal class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic/LimitedPrecisionReal.st,v 1.37 2001-05-17 22:08:24 stefan Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/LimitedPrecisionReal.st,v 1.38 2001-06-26 14:03:35 cg Exp $'
 ! !