The F# Power Pack includes an arbitrarily large Rational type called BigNum, which in turn uses the arbitrarily large BigInt type for the numerator and denominator. The BigInteger type has recently moved into .Net 4.0 under the System.Numerics namespace, It would be nice if BigNum could join it there.

What if you want a fixed size Rational implementation that can be used from any .Net language? The following is a sample F# implementation using Int64 for the numerator and denominator values:

open System type Rational64 (numerator:Int64,denominator:Int64) = do if denominator = 0L && numerator <> 0L then raise (new DivideByZeroException()) let recgcd x y = match y with | 0L -> x | _ -> gcd y (x % y) let norm = let u = int64 (sign denominator) * numerator let v = abs denominator let d = gcd (abs u) v if denominator <> 0L then u / d, v / d else numerator, denominator let numerator, denominator = norm static let zero = Rational64(0L,1L) static let Op f (a:Rational64,b:Rational64) = let x,y = a.Numerator, a.Denominator let u,v = b.Numerator, b.Denominator f x y u v static let Add = Op (fun x y u v -> new Rational64(x * v + u * y, y * v)) static let Sub = Op (fun x y u v -> new Rational64(x * v - u * y, y * v)) static let Mul = Op (fun x y u v -> new Rational64(x * u, y * v)) static let Div = Op (fun x y u v -> new Rational64(x * v, y * u)) static let Eq = Op (fun x y u v -> x * v = u * y) static let Lt = Op (fun x y u v -> x * v < u * y) static let Le = Op (fun x y u v -> x * v <= u * y) static let Gt = Op (fun x y u v -> x * v > u * y) static let Ge = Op (fun x y u v -> x * v >= u * y) static let Compare (a:Rational64,b:Rational64) = if Lt (a,b) then -1 else if Gt (a,b) then 1 else 0 new(numerator:Int64) = Rational64(numerator,1L) member this.Numerator = numerator member this.Denominator = denominator static member Zero = zero member this.ToDecimal() = decimal this.Numerator / decimal this.Denominator member this.ToDouble() = double this.Numerator / double this.Denominator override this.ToString() = match numerator, denominator with | n, 1L -> n.ToString() | n, d -> sprintf "%d/%d" n d static member ( + ) (a,b) = Add (a,b) static member ( - ) (a,b) = Sub (a,b) static member ( * ) (a,b) = Mul (a,b) static member ( / ) (a,b) = Div (a,b) static member op_Equality (a,b) = Eq (a,b) static member op_Inequality (a,b) = not (Eq (a,b)) static member op_LessThan (a,b) = Lt (a,b) static member op_LessThanOrEqual (a,b) = Le (a,b) static member op_GreaterThan (a,b) = Gt (a,b) static member op_GreaterThanOrEqual (a,b) = Ge (a,b) interface IComparable with member this.CompareTo (x) = Compare (this, x :?> Rational64) static member Equals(a,b) = Eq(a,b) override this.Equals x = this = (x :?> Rational64)

Finally specific for F# by implementing a NumericLiteralX module it is possible to declare Rational64 literals like so (Q for Quotient):

module NumericLiteralQ = let FromZero () = Rational64(0L) let FromOne () = Rational64(1L) let FromInt32 (x) = Rational64(int64 x) let FromInt64 (x:Int64) = Rational64(x) module Test = let x = 42Q