Fraction.st
changeset 7355 96f466eeddf5
parent 7140 f65ec31fc081
child 7380 c704ff45bb80
--- a/Fraction.st	Fri Jun 13 20:52:21 2003 +0200
+++ b/Fraction.st	Mon Jun 16 11:14:59 2003 +0200
@@ -207,32 +207,21 @@
 * aNumber
     "return the product of the receiver and the argument, aNumber"
 
-    |n d|
-
-    "/
     "/ notice:
     "/ the following code handles some common cases,
     "/ and exists as an optimization, to speed up those cases.
+    "/ also notice, that checks for those cases must be inlinable without
+    "/ a message send; otherwise double-dispatch is just as fast.
     "/
     "/ Conceptionally, (and for most other argument types),
     "/ mixed arithmetic is implemented by double dispatching
     "/ (see the message send at the bottom)
-    "/
 
-    aNumber isInteger ifTrue:[
+    (aNumber isMemberOf:SmallInteger) ifTrue:[
         ^ self class 
                 numerator:(numerator * aNumber)
                 denominator:denominator
     ].
-    aNumber isFraction ifTrue:[
-        aNumber isFixedPoint ifFalse:[  "/ the value was corrent, but the scale is lost
-            n := numerator * aNumber numerator.
-            d := denominator * aNumber denominator.
-            ^ self class 
-                numerator:n 
-                denominator:d
-        ]
-    ].
     (aNumber isMemberOf:Float) ifTrue:[
         ^ (numerator * aNumber) / denominator
     ].
@@ -245,42 +234,23 @@
 + aNumber
     "return the sum of the receiver and the argument, aNumber"
 
-    |n d|
-
-    "/
     "/ notice:
     "/ the following code handles some common cases,
     "/ and exists as an optimization, to speed up those cases.
+    "/ also notice, that checks for those cases must be inlinable without
+    "/ a message send; otherwise double-dispatch is just as fast.
     "/
     "/ Conceptionally, (and for most other argument types),
     "/ mixed arithmetic is implemented by double dispatching
     "/ (see the message send at the bottom)
-    "/
 
-    aNumber isInteger ifTrue:[
+    (aNumber isMemberOf:SmallInteger) ifTrue:[
         ^ self class 
-                numerator:(numerator + (denominator * aNumber))
-                denominator:denominator
-    ].
-    aNumber isFraction ifTrue:[
-        aNumber isFixedPoint ifFalse:[  "/ the value was correct, but the scale is lost
-            n := aNumber numerator.
-            d := aNumber denominator.
-
-            "save a multiplication if possible"
-            denominator == d ifTrue:[
-                n := numerator + n
-            ] ifFalse:[
-                n := (numerator * d) + (n * denominator).
-                d := denominator * d.
-            ].
-            ^ self class 
-                numerator:n 
-                denominator:d
-        ]
+            numerator:(numerator + (denominator * aNumber))
+            denominator:denominator
     ].
     (aNumber isMemberOf:Float) ifTrue:[
-        ^ aNumber + self asFloat
+        ^ (numerator asFloat / denominator asFloat) + aNumber
     ].
 
     ^ aNumber sumFromFraction:self
@@ -291,40 +261,21 @@
 - aNumber
     "return the difference of the receiver and the argument, aNumber"
 
-    |n d|
-
-    "/
     "/ notice:
     "/ the following code handles some common cases,
     "/ and exists as an optimization, to speed up those cases.
+    "/ also notice, that checks for those cases must be inlinable without
+    "/ a message send; otherwise double-dispatch is just as fast.
     "/
     "/ Conceptionally, (and for most other argument types),
     "/ mixed arithmetic is implemented by double dispatching
     "/ (see the message send at the bottom)
-    "/
 
-    aNumber isInteger ifTrue:[
+    (aNumber isMemberOf:SmallInteger) ifTrue:[
         ^ self class 
                 numerator:(numerator - (denominator * aNumber))
                 denominator:denominator
     ].
-    aNumber isFraction ifTrue:[
-        aNumber isFixedPoint ifFalse:[  "/ the value was corrent, but the scale is lost
-            n := aNumber numerator.
-            d := aNumber denominator.
-
-            "save a multiplication if possible"
-            denominator == d ifTrue:[
-                n := numerator - n
-            ] ifFalse:[
-                n := (numerator * d) - (n * denominator).
-                d := denominator * d
-            ].
-            ^ self class 
-                numerator:n 
-                denominator:d
-        ]
-    ].
     (aNumber isMemberOf:Float) ifTrue:[
         ^ (numerator asFloat / denominator asFloat) - aNumber
     ].
@@ -345,32 +296,21 @@
 / aNumber
     "return the quotient of the receiver and the argument, aNumber"
 
-    |n d|
-
-    "/
     "/ notice:
     "/ the following code handles some common cases,
     "/ and exists as an optimization, to speed up those cases.
+    "/ also notice, that checks for those cases must be inlinable without
+    "/ a message send; otherwise double-dispatch is just as fast.
     "/
     "/ Conceptionally, (and for most other argument types),
     "/ mixed arithmetic is implemented by double dispatching
     "/ (see the message send at the bottom)
-    "/
 
-    aNumber isInteger ifTrue:[
+    (aNumber isMemberOf:SmallInteger) ifTrue:[
         ^ self class 
                 numerator:numerator
                 denominator:(denominator * aNumber)
     ].
-    aNumber isFraction ifTrue:[
-        aNumber isFixedPoint ifFalse:[  "/ the value was corrent, but the scale is lost
-            n := numerator * aNumber denominator.
-            d := denominator * aNumber numerator.
-            ^ self class 
-                numerator:n 
-                denominator:d
-        ]
-    ].
     (aNumber isMemberOf:Float) ifTrue:[
         ^ numerator / (denominator * aNumber)
     ].
@@ -514,6 +454,51 @@
     ^ self asInteger asLargeInteger
 !
 
+asLongFloat
+    "return a long float with (approximately) my value"
+
+    |num den numShift denShift bits|
+
+    (numerator class == SmallInteger and:[denominator class == SmallInteger]) ifTrue:[
+        ^ (numerator asLongFloat) / (denominator asLongFloat)
+    ].
+
+    "Do it the hard way: reduce magnitude and undo reduction on the quotient"
+
+    bits := LongFloat precision * 2.    "number of bits to preserve (conservative)"
+    num := numerator abs.
+    numShift := bits - num highBit. "(num highBit - bits) negated"
+    numShift < 0 ifTrue:[num := num bitShift:numShift] ifFalse:[numShift := 0].
+
+    den :=  denominator.
+    denShift := bits - den highBit. "(den highBit - bits) negated"
+    denShift < 0 ifTrue:[den := den bitShift:denShift] ifFalse:[denShift := 0].
+
+    ^ (num asLongFloat / den asLongFloat) * (2.0 raisedToInteger:denShift-numShift) * (num sign)
+
+    " 
+      (5/9) asLongFloat
+      (-5/9) asLongFloat
+      (500000000000/900000000000) asLongFloat
+      (-500000000000/900000000000) asLongFloat
+      (500000000000/9) asLongFloat
+      (5/900000000000) asLongFloat
+      89012345678901234567 asFloat / 123456789123456789 asLongFloat
+      (89012345678901234567 / 123456789123456789) asLongFloat
+
+      (
+       180338700661043257034670206806167960222709397862806840937993331366591676308781197477183367018067356365812757479444845320188679437752013593674158587947149815441890236037219685250845721864713487208757788709113534916165172927384095182655935222723385253851776639985379367854545495930551624041981995105743408203125
+        /
+       180331613628627651967947866455016278082980736719853750685591387625058011528928110602436691256100991596843001549483950600930062886280582766771424470965440873615557144641435276844465734361353086032476712374317224249252177316815544331763696909434844464464323192083930469387098582956241443753242492675781250
+      ) asLongFloat
+
+      180338700661043257034670206806167960222709397862806840937993331366591676308781197477183367018067356365812757479444845320188679437752013593674158587947149815441890236037219685250845721864713487208757788709113534916165172927384095182655935222723385253851776639985379367854545495930551624041981995105743408203125
+         asLongFloat /
+      180331613628627651967947866455016278082980736719853750685591387625058011528928110602436691256100991596843001549483950600930062886280582766771424470965440873615557144641435276844465734361353086032476712374317224249252177316815544331763696909434844464464323192083930469387098582956241443753242492675781250
+         asLongFloat
+    "
+!
+
 asShortFloat
     "return a short float with (approximately) my value"
 
@@ -548,21 +533,9 @@
     "return true if the receiver is less
      than aNumber, false otherwise."
 
-    |d n|
-
     (aNumber isMemberOf:SmallInteger) ifTrue:[
         ^ numerator < (denominator * aNumber)
     ].
-    aNumber isFraction ifTrue:[
-        d := aNumber denominator.
-        n := aNumber numerator.
-
-        "/ save a multiplication if possible
-        d == denominator ifTrue:[
-            ^ numerator < n
-        ].
-        ^ (numerator * d) < (denominator * n)
-    ].
     ^ aNumber lessFromFraction:self
 
     "Modified: 5.11.1996 / 10:30:52 / cg"
@@ -578,19 +551,7 @@
         ].
         ^ numerator = aNumber
     ].
-    aNumber isFraction ifTrue:[
-        denominator = aNumber denominator ifTrue:[
-            ^ numerator = aNumber numerator
-        ].
-        ^ self sameFractionValueAs:aNumber
-    ].
-    (aNumber isInteger) ifTrue:[
-        (denominator == 1) ifFalse:[
-            ^ numerator = (aNumber * denominator)
-        ].
-        ^ numerator = aNumber
-    ].
-    ^ self retry:#= coercing:aNumber
+    ^ aNumber equalFromFraction:self
 
     "Modified: / 7.7.1998 / 17:17:07 / cg"
 !
@@ -600,24 +561,10 @@
      than aNumber, false otherwise."
     "optional - could use inherited method ..."
 
-    |d n|
-
     (aNumber isMemberOf:SmallInteger) ifTrue:[
         ^ numerator > (denominator * aNumber)
     ].
-    aNumber isFraction ifTrue:[
-        d := aNumber denominator.
-        n := aNumber numerator.
-
-        "/ save a multiplication if possible
-        d == denominator ifTrue:[
-            ^ numerator > n
-        ].
-        ^ (numerator * d) > (denominator * n)
-    ].
-    ^ self retry:#> coercing:aNumber
-
-    "Modified: 5.11.1996 / 10:31:28 / cg"
+    ^ aNumber < self
 !
 
 hash
@@ -658,72 +605,249 @@
 
 !Fraction methodsFor:'double dispatching'!
 
+differenceFromFixedPoint:aFixedPoint
+    |n d otherDenominator otherNumerator|
+
+    otherDenominator := aFixedPoint denominator.
+    otherNumerator := aFixedPoint numerator.
+
+    "save a multiplication if possible"
+    otherDenominator == denominator ifTrue:[
+        n := otherNumerator - numerator. 
+        d := otherDenominator.
+    ] ifFalse:[
+        n := (otherNumerator * denominator) - (numerator * otherDenominator).
+        d := otherDenominator * denominator.
+    ].
+    ^ aFixedPoint class 
+        numerator:n
+        denominator:d
+        scale:(aFixedPoint scale)
+
+    "
+     ((1/3) asFixedPoint:2) - (1/3)        
+     ((1/3) asFixedPoint:2) - (2/3) 
+    "
+!
+
 differenceFromFloat:aFloat
     "sent when a float does not know how to subtract the receiver, a fraction"
 
     ^ (aFloat * denominator - numerator) / denominator
 !
 
+differenceFromFraction:aFraction
+    |n d otherDenominator otherNumerator|
+
+    otherDenominator := aFraction denominator.
+    otherNumerator := aFraction numerator.
+
+    "save a multiplication if possible"
+    otherDenominator == denominator ifTrue:[
+        n := otherNumerator - numerator. 
+        d := otherDenominator.
+    ] ifFalse:[
+        n := (otherNumerator * denominator) - (numerator * otherDenominator).
+        d := otherDenominator * denominator.
+    ].
+    ^ aFraction class 
+        numerator:n
+        denominator:d
+
+    "
+     ((1/3) asFixedPoint:2) - (1/3)        
+     ((1/3) asFixedPoint:2) - (2/3) 
+    "
+!
+
 differenceFromInteger:anInteger
     "sent when an integer does not know how to subtract the receiver, a fraction"
 
-    ^ (self class 
+    ^ self class 
         numerator:((anInteger * denominator) - numerator)
-        denominator:denominator)
+        denominator:denominator
 
     "Modified: 28.7.1997 / 19:08:53 / cg"
 !
 
+equalFromFraction:aFraction
+    denominator = aFraction denominator ifFalse:[^ false].
+    ^ numerator = aFraction numerator
+!
+
+equalFromInteger:anInteger
+    "sent when an integer does not know how to compare to the receiver, a fraction"
+
+    "as I am always reduced, this test should not be required.
+     However, it is here for subclasses and to allow comparing unnormalized fractions,
+     which might be encountered internally"
+
+    denominator = 1 ifFalse:[^ false].
+    ^ numerator = anInteger
+!
+
+lessFromFraction:aFraction
+    "sent when a fraction does not know how to compare to the receiver"
+
+    |n d|
+
+    d := aFraction denominator.
+    n := aFraction numerator.
+
+    "/ save a multiplication if possible
+    d == denominator ifTrue:[
+        ^ n < numerator
+    ].
+    ^ (denominator * n) < (numerator * d)  
+!
+
 lessFromInteger:anInteger
     "sent when an integer does not know how to compare to the receiver, a fraction"
 
     ^ (denominator * anInteger) < numerator
 !
 
+productFromFixedPoint:aFixedPoint
+    ^ aFixedPoint class 
+        numerator:(aFixedPoint numerator * numerator) 
+        denominator:(aFixedPoint denominator * denominator)
+        scale:(aFixedPoint scale)
+
+    "
+     ((1/3) asFixedPoint:2) * 2       
+     ((1/3) asFixedPoint:2) * (1/2) 
+     ((1/3) asFixedPoint:2) * (3/2) 
+    "
+!
+
 productFromFloat:aFloat
     "sent when a float does not know how to multiply the receiver, a fraction"
 
     ^ aFloat * numerator / denominator
 !
 
+productFromFraction:aFraction
+    ^ aFraction class 
+        numerator:(aFraction numerator * numerator) 
+        denominator:(aFraction denominator * denominator)
+
+    "
+     ((1/3) asFixedPoint:2) * 2       
+     ((1/3) asFixedPoint:2) * (1/2) 
+     ((1/3) asFixedPoint:2) * (3/2) 
+    "
+!
+
 productFromInteger:anInteger
     "sent when an integer does not know how to multiply the receiver, a fraction"
 
-    ^ (self class 
+    ^ self class 
         numerator:(anInteger * numerator)
-        denominator:denominator)
+        denominator:denominator
 
     "Modified: 28.7.1997 / 19:06:22 / cg"
 !
 
+quotientFromFixedPoint:aFixedPoint
+    ^ aFixedPoint class 
+        numerator:(aFixedPoint numerator * denominator) 
+        denominator:(aFixedPoint denominator * numerator)
+        scale:(aFixedPoint scale)
+
+    "
+     ((1/3) asFixedPoint:2) / 2       
+     ((1/3) asFixedPoint:2) / (1/2) 
+    "
+!
+
 quotientFromFloat:aFloat
     "sent when a float does not know how to divide by the receiver, a fraction"
 
     ^ (aFloat * denominator) / numerator
 !
 
+quotientFromFraction:aFraction
+    ^ aFraction class 
+        numerator:(aFraction numerator * denominator) 
+        denominator:(aFraction denominator * numerator)
+
+    "
+     (1/3) / (1/2) 
+     (1/3) / (3/2) 
+    "
+!
+
 quotientFromInteger:anInteger
     "sent when an integer does not know how to divide by the receiver, a fraction"
 
-    ^ (self class 
+    ^ self class 
         numerator:(anInteger * denominator)
-        denominator:numerator)
+        denominator:numerator
 
     "Modified: 28.7.1997 / 19:08:46 / cg"
 !
 
+sumFromFixedPoint:aFixedPoint
+    |n d otherDenominator otherNumerator|
+
+    otherDenominator := aFixedPoint denominator.
+    otherNumerator := aFixedPoint numerator.
+
+    "save a multiplication if possible"
+    otherDenominator == denominator ifTrue:[
+        n := otherNumerator + numerator. 
+        d := otherDenominator.
+    ] ifFalse:[
+        n := (otherNumerator * denominator) + (numerator * otherDenominator).
+        d := otherDenominator * denominator.
+    ].
+    ^ aFixedPoint class 
+        numerator:n
+        denominator:d
+        scale:(aFixedPoint scale)
+
+    "
+     ((1/3) asFixedPoint:2) + (1/3)        
+     ((1/3) asFixedPoint:2) + (2/3) 
+    "
+!
+
 sumFromFloat:aFloat
     "sent when a float does not know how to add the receiver, a fraction"
 
     ^ (aFloat * denominator + numerator) / denominator
 !
 
+sumFromFraction:aFraction
+    |n d otherDenominator otherNumerator|
+
+    otherDenominator := aFraction denominator.
+    otherNumerator := aFraction numerator.
+
+    "save a multiplication if possible"
+    otherDenominator == denominator ifTrue:[
+        n := otherNumerator + numerator. 
+        d := otherDenominator.
+    ] ifFalse:[
+        n := (otherNumerator * denominator) + (numerator * otherDenominator).
+        d := otherDenominator * denominator.
+    ].
+    ^ aFraction class 
+        numerator:n
+        denominator:d
+
+    "
+     (1/3) + (1/3)        
+     (1/3) + (2/3) 
+    "
+!
+
 sumFromInteger:anInteger
     "sent when an integer does not know how to add the receiver, a fraction"
 
-    ^ (self class 
+    ^ self class 
         numerator:(numerator + (anInteger * denominator))
-        denominator:denominator)
+        denominator:denominator
 
     "Modified: 28.7.1997 / 19:08:40 / cg"
 ! !
@@ -836,17 +960,24 @@
 !Fraction methodsFor:'truncation & rounding'!
 
 fractionPart
-    "extract the after-decimal fraction part."
+    "extract the after-decimal fraction part,
+     such that (self truncated + self fractionPart) = self"
 
     numerator abs < denominator abs ifTrue:[
         ^ self
     ].
-    ^ super fractionPart
+    ^ (numerator rem: denominator) / denominator
+    "/ ^ super fractionPart
 
     "
-     (3/2) fractionPart 
+     (3/2) fractionPart + (3/2) truncated    
+     (-3/2) fractionPart + (-3/2) truncated    
+
+     (3/2) fractionPart     
      (-3/2) fractionPart     
-     (2/3) fractionPart 
+     (3/2) asFloat fractionPart     
+     (-3/2) asFloat fractionPart     
+     (2/3) fractionPart          
      ((3/2)*(15/4)) fractionPart   
      ((2/3)*(4/15)) fractionPart   
     "
@@ -917,7 +1048,7 @@
 !Fraction class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic/Fraction.st,v 1.62 2003-03-31 09:12:58 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/Fraction.st,v 1.63 2003-06-16 09:14:59 cg Exp $'
 ! !
 
 Fraction initialize!