"
COPYRIGHT (c) 1989 by Claus Gittinger
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
"{ Package: 'stx:libbasic' }"
Number subclass:#Fraction
instanceVariableNames:'numerator denominator'
classVariableNames:'FractionOne FractionZero PrintWholeNumbers'
poolDictionaries:''
category:'Magnitude-Numbers'
!
!Fraction class methodsFor:'documentation'!
copyright
"
COPYRIGHT (c) 1989 by Claus Gittinger
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
!
documentation
"
Instances of Fraction represent fractional numbers consisting of
a numerator and denominator. Both are themselfes arbitrary precision
integers.
Fractions are usually created by dividing Integers using / (for exact division).
Notice, that all operations on fractions reduce their result; this means, that
the result of a fraction-operation may return an integer.
Aka:
(1 / 7) * 7 -> 1 (not 0.99999999...)
Mixed mode arithmetic:
fraction op fraction -> fraction/integer
fraction op fix -> fix; scale is fix's scale
fraction op integer -> fraction/integer
fraction op float -> float
[classVariables:]
PrintWholeNumbers Booolean experimental:
controls how fractions which are greater than 1 are printed.
if true, print them as a sum of an integral and the fractional part.
(Large ones are easier to read this way)
(17/3) printString -> '(5+(2/3))'
for now, the default is false, for backward compatibility
[author:]
Claus Gittinger
[see also:]
Number
Float Integer FixedPoint
"
! !
!Fraction class methodsFor:'initialization'!
initialize
FractionZero isNil ifTrue:[
FractionZero := self numerator:0 denominator:1.
FractionOne := self numerator:1 denominator:1
]
! !
!Fraction class methodsFor:'instance creation'!
new
"create and return a new fraction with value 0"
^ self numerator:0 denominator:1
!
numerator:num denominator:den
"create and return a new fraction with numerator num and denominator den"
|newFraction|
%{ /* NOCONTEXT */
/* this check allows subclassing .. */
if (self == Fraction) {
if (__bothSmallInteger(num, den)) {
if (__CanDoQuickAlignedNew(sizeof(struct __Fraction))) { /* OBJECT ALLOCATION */
OBJ newFraction;
int spc;
INT iDen;
__qCheckedAlignedNew(newFraction, sizeof(struct __Fraction));
__InstPtr(newFraction)->o_class = self;
iDen = __intVal(den);
if (iDen != 0) {
if (iDen < 0) {
__FractionInstPtr(newFraction)->f_numerator = __MKSMALLINT(- __intVal(num));
__FractionInstPtr(newFraction)->f_denominator = __MKSMALLINT(- iDen);
} else {
__FractionInstPtr(newFraction)->f_numerator = num;
__FractionInstPtr(newFraction)->f_denominator = den;
}
if (num == __MKSMALLINT(1)) {
/* no need to reduce */
RETURN ( newFraction );
}
}
}
}
}
%}.
den == 0 ifTrue:[
^ ZeroDivide raiseRequestWith:thisContext.
].
newFraction isNil ifTrue:[
newFraction := self basicNew setNumerator:num denominator:den.
].
^ newFraction reduced
"
Fraction numerator:1 denominator:3
Fraction numerator:2 denominator:6
Fraction numerator:1 denominator:0
Fraction numerator:2 denominator:0
"
! !
!Fraction class methodsFor:'constants'!
pi
"return the constant pi as Fraction"
^ self
numerator: 314159265358979323846264343
denominator:100000000000000000000000000
"/ ^ self
"/ numerator: 314159265358979323846264338327950288419716939937510582097494459
"/ denominator:100000000000000000000000000000000000000000000000000000000000000
"
Fraction pi
Fraction pi asFloat
Float pi
"
"Modified: 5.11.1996 / 11:11:44 / cg"
!
unity
"return the neutral element for multiplication (1 / 1)"
^ FractionOne
"Modified: 18.7.1996 / 12:26:06 / cg"
!
zero
"return the neutral element for addition (0 / 1)"
^ FractionZero
"Modified: 18.7.1996 / 12:26:12 / cg"
! !
!Fraction class methodsFor:'queries'!
isBuiltInClass
"return true if this class is known by the run-time-system.
Here, true is returned for myself, false for subclasses."
^ self == Fraction
"Modified: 23.4.1996 / 15:59:10 / cg"
! !
!Fraction methodsFor:'accessing'!
denominator
"return the denominator"
^ denominator
!
numerator
"return the numerator"
^ numerator
! !
!Fraction methodsFor:'arithmetic'!
* 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.
"/
"/ Conceptionally, (and for most other argument types),
"/ mixed arithmetic is implemented by double dispatching
"/ (see the message send at the bottom)
"/
aNumber isInteger 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
].
^ aNumber productFromFraction:self
"Modified: 28.7.1997 / 19:09:23 / cg"
!
+ 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.
"/
"/ Conceptionally, (and for most other argument types),
"/ mixed arithmetic is implemented by double dispatching
"/ (see the message send at the bottom)
"/
aNumber isInteger 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
]
].
(aNumber isMemberOf:Float) ifTrue:[
^ aNumber + self asFloat
].
^ aNumber sumFromFraction:self
"Modified: 28.7.1997 / 19:09:16 / cg"
!
- 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.
"/
"/ Conceptionally, (and for most other argument types),
"/ mixed arithmetic is implemented by double dispatching
"/ (see the message send at the bottom)
"/
aNumber isInteger 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
].
^ aNumber differenceFromFraction:self
"
(1/3) - (1/9)
(1/9) - (1/3)
(999/1000) - (1/1000)
(999/1000) - (1/1000000)
(999000/1000000) - (1/1000000)
"
"Modified: 28.7.1997 / 19:09:11 / cg"
!
/ 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.
"/
"/ Conceptionally, (and for most other argument types),
"/ mixed arithmetic is implemented by double dispatching
"/ (see the message send at the bottom)
"/
aNumber isInteger 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)
].
^ aNumber quotientFromFraction:self
"Modified: 28.7.1997 / 19:09:06 / cg"
!
// aNumber
"return the integer quotient of dividing the receiver by aNumber with
truncation towards negative infinity."
^ (numerator * aNumber denominator) // (denominator * aNumber numerator)
"
0.5 // 1
-0.5 // 1
(1/2) // 1 = 0 ifFalse:[self halt].
(-1/2) // 1 = -1 ifFalse:[self halt].
"
"Modified: / 5.11.1996 / 11:47:14 / cg"
"Modified: / 13.2.1998 / 09:15:35 / stefan"
!
negated
"optional - could use inherited method ..."
^ self class
numerator:(numerator negated)
denominator:denominator
"Modified: 5.11.1996 / 10:29:11 / cg"
!
reciprocal
"optional - could use inherited method ..."
numerator == 1 ifTrue:[^ denominator].
^ self class
numerator:denominator
denominator:numerator
"Modified: 5.11.1996 / 10:29:22 / cg"
! !
!Fraction methodsFor:'coercing & converting'!
asFixedPoint
"return the receiver as fixedPoint number.
Q: what should the scale be here ?"
^ FixedPoint numerator:numerator denominator:denominator scale:2
"
(1/2) asFixedPoint
"
"Created: 5.11.1996 / 15:15:54 / cg"
!
asFixedPoint:scale
"return the receiver as fixedPoint number, with the given number
of post-decimal-point digits."
^ FixedPoint numerator:numerator denominator:denominator scale:scale
"
(1/2) asFixedPoint:2
(1/3) asFixedPoint:2
(1/3) asFixedPoint:5
(2/3) asFixedPoint:2
(2/3) asFixedPoint:5
"
"Created: 5.11.1996 / 15:15:54 / cg"
"Modified: 10.1.1997 / 19:54:50 / cg"
!
asFloat
"return a float with (approximately) my value"
|num den numShift denShift bits|
(numerator class == SmallInteger and:[denominator class == SmallInteger]) ifTrue:[
^ (numerator asFloat) / (denominator asFloat)
].
"Do it the hard way: reduce magnitude and undo reduction on the quotient"
bits := Float 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 asFloat / den asFloat) * (2.0 raisedToInteger:denShift-numShift) * (num sign)
"
(5/9) asFloat
(-5/9) asFloat
(500000000000/900000000000) asFloat
(-500000000000/900000000000) asFloat
(500000000000/9) asFloat
(5/900000000000) asFloat
89012345678901234567 asFloat / 123456789123456789 asFloat
(89012345678901234567 / 123456789123456789) asFloat
(
180338700661043257034670206806167960222709397862806840937993331366591676308781197477183367018067356365812757479444845320188679437752013593674158587947149815441890236037219685250845721864713487208757788709113534916165172927384095182655935222723385253851776639985379367854545495930551624041981995105743408203125
/
180331613628627651967947866455016278082980736719853750685591387625058011528928110602436691256100991596843001549483950600930062886280582766771424470965440873615557144641435276844465734361353086032476712374317224249252177316815544331763696909434844464464323192083930469387098582956241443753242492675781250
) asFloat
180338700661043257034670206806167960222709397862806840937993331366591676308781197477183367018067356365812757479444845320188679437752013593674158587947149815441890236037219685250845721864713487208757788709113534916165172927384095182655935222723385253851776639985379367854545495930551624041981995105743408203125
asFloat /
180331613628627651967947866455016278082980736719853750685591387625058011528928110602436691256100991596843001549483950600930062886280582766771424470965440873615557144641435276844465734361353086032476712374317224249252177316815544331763696909434844464464323192083930469387098582956241443753242492675781250
asFloat
"
!
asFraction
"return the receiver as fraction - thats itself"
^ self
!
asInteger
"return an integer with my value - will usually truncate"
^ numerator // denominator
!
asLargeInteger
"return an integer with my value - will usually truncate"
^ self asInteger asLargeInteger
!
asShortFloat
"return a short float with (approximately) my value"
(numerator class == SmallInteger and:[denominator class == SmallInteger]) ifTrue:[
^ (numerator asShortFloat) / (denominator asShortFloat)
].
^ self asFloat asShortFloat
"
(5/9) asShortFloat
(500000000000/900000000000) asShortFloat
(500000000000/9) asShortFloat
"
!
coerce:aNumber
"return aNumber converted into receivers type"
^ aNumber asFraction
!
generality
"return the generality value - see ArithmeticValue>>retry:coercing:"
^ 60
! !
!Fraction methodsFor:'comparing'!
< aNumber
"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"
!
= aNumber
"return true, if the argument represents the same numeric value
as the receiver, false otherwise"
(aNumber isMemberOf:SmallInteger) ifTrue:[
(denominator == 1) ifFalse:[
^ numerator = (aNumber * denominator)
].
^ 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
"Modified: / 7.7.1998 / 17:17:07 / cg"
!
> aNumber
"return true if the receiver is greater
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"
!
hash
"return a number for hashing; redefined, since fractions compare
by numeric value (i.e. (9/3) = 3), therefore (9/3) hash must be the same
as 3 hash."
(denominator = 1) ifTrue:[^ numerator hash].
^ self asFloat hash
"
3 hash
(9/3) hash
3.0 hash
(1/2) hash
(1/4) hash
0.0 hash
0.5 hash
0.25 hash
0.4 hash
"
!
sameFractionValueAs:aNumber
"return true, if the argument represents the same numeric value
as the receiver, false otherwise"
|rSelf rNum|
rSelf := self reduced.
rNum := aNumber reduced.
rSelf denominator = rNum denominator ifTrue:[
^ rSelf numerator = rNum numerator
].
^ false
! !
!Fraction methodsFor:'double dispatching'!
differenceFromFloat:aFloat
"sent when a float does not know how to subtract the receiver, a fraction"
^ (aFloat * denominator - numerator) / denominator
!
differenceFromInteger:anInteger
"sent when an integer does not know how to subtract the receiver, a fraction"
^ (self class
numerator:((anInteger * denominator) - numerator)
denominator:denominator)
"Modified: 28.7.1997 / 19:08:53 / cg"
!
lessFromInteger:anInteger
"sent when an integer does not know how to compare to the receiver, a fraction"
^ (denominator * anInteger) < numerator
!
productFromFloat:aFloat
"sent when a float does not know how to multiply the receiver, a fraction"
^ aFloat * numerator / denominator
!
productFromInteger:anInteger
"sent when an integer does not know how to multiply the receiver, a fraction"
^ (self class
numerator:(anInteger * numerator)
denominator:denominator)
"Modified: 28.7.1997 / 19:06:22 / cg"
!
quotientFromFloat:aFloat
"sent when a float does not know how to divide by the receiver, a fraction"
^ (aFloat * denominator) / numerator
!
quotientFromInteger:anInteger
"sent when an integer does not know how to divide by the receiver, a fraction"
^ (self class
numerator:(anInteger * denominator)
denominator:numerator)
"Modified: 28.7.1997 / 19:08:46 / cg"
!
sumFromFloat:aFloat
"sent when a float does not know how to add the receiver, a fraction"
^ (aFloat * denominator + numerator) / denominator
!
sumFromInteger:anInteger
"sent when an integer does not know how to add the receiver, a fraction"
^ (self class
numerator:(numerator + (anInteger * denominator))
denominator:denominator)
"Modified: 28.7.1997 / 19:08:40 / cg"
! !
!Fraction methodsFor:'encoding'!
encodeOn:anEncoder with:aParameter
anEncoder encodeFraction:self with:aParameter
! !
!Fraction methodsFor:'printing & storing'!
printOn:aStream
"append a printed representation of the receiver to the
argument, aStream"
|t|
PrintWholeNumbers == true ifTrue:[
"/ experimental: print fractions which are greater than 1 as a sum of
"/ an integral and the fractional part. They are easier to read this way.
numerator > denominator ifTrue:[
aStream nextPut:$(.
t := numerator // denominator.
t printOn:aStream.
aStream nextPutAll:'+('.
(numerator - (t*denominator)) printOn:aStream.
aStream nextPut:$/.
denominator printOn:aStream.
aStream nextPutAll:'))'.
^ self
].
].
aStream nextPut:$(.
numerator printOn:aStream.
aStream nextPut:$/.
denominator printOn:aStream.
aStream nextPut:$)
"Modified: / 31.7.2002 / 09:56:41 / cg"
! !
!Fraction methodsFor:'private'!
reduced
"reduce the receiver; divide the numerator and denominator by their
greatest common divisor; if the result is integral, return an Integer.
Otherwise, return the normalized receiver.
(i.e. this is destructive wrt. the instance values)"
|gcd den|
den := denominator.
den < 0 ifTrue:[
numerator := numerator negated.
den := denominator := den negated.
].
den == 1 ifTrue:[^ numerator].
numerator == 1 ifTrue:[^ self].
numerator == 0 ifTrue:[^ 0].
gcd := numerator gcd:den.
(gcd ~~ 1) ifTrue:[
gcd < 0 ifTrue:[
gcd := gcd negated.
].
numerator := numerator // gcd.
denominator := den := den // gcd.
(den == 1) ifTrue:[^ numerator].
].
^ self
!
setNumerator:num denominator:den
"set both numerator and denominator"
numerator := num.
denominator := den
! !
!Fraction methodsFor:'testing'!
isFraction
"return true, if the receiver is some kind of fraction;
true is returned here - the method is redefined from Object."
^ true
!
isLiteral
"return true, if the receiver can be used as a literal constant in ST syntax
(i.e. can be used in constant arrays)"
^ true
!
negative
"return true if the receiver is negative"
(numerator < 0) ifTrue:[
^ (denominator < 0) not
].
^ (denominator < 0)
! !
!Fraction methodsFor:'truncation & rounding'!
fractionPart
"extract the after-decimal fraction part."
numerator abs < denominator abs ifTrue:[
^ self
].
^ super fractionPart
"
(3/2) fractionPart
(-3/2) fractionPart
(2/3) fractionPart
((3/2)*(15/4)) fractionPart
((2/3)*(4/15)) fractionPart
"
"Modified: / 5.11.2001 / 17:55:25 / cg"
!
integerPart
"extract the pre-decimal integer part."
numerator abs < denominator abs ifTrue:[
^ 0
].
^ super integerPart
"
(3/2) integerPart
(-3/2) integerPart
(2/3) integerPart
((3/2)*(15/4)) integerPart
((2/3)*(4/15)) integerPart
"
"Modified: / 5.11.2001 / 17:55:01 / cg"
!
rounded
"return the receiver rounded to the nearest integer as integer"
"/ mhmh - what about -(1/2)
|t|
self negative ifTrue:[
t := self - (1/2)
] ifFalse:[
t := self + (1/2)
].
^ t truncated.
"
(1/3) rounded
(1/3) negated rounded
(1/2) rounded
(1/2) negated rounded
0.5 rounded
-0.5 rounded
(2/3) rounded
(2/3) negated rounded
"
"Modified: 5.11.1996 / 11:32:32 / cg"
!
truncated
"return the receiver truncated towards zero as Integer"
^ numerator quo: denominator
"
(3/2) truncated
(3/2) negated truncated
"
"Modified: 5.11.1996 / 12:18:46 / cg"
! !
!Fraction class methodsFor:'documentation'!
version
^ '$Header: /cvs/stx/stx/libbasic/Fraction.st,v 1.62 2003-03-31 09:12:58 cg Exp $'
! !
Fraction initialize!