Phillip Trelford's Array

POKE 36879,255

F# Rational64 implementation

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 rec gcd 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

 

Rational64.fs (4.90 kb)

Rational64Test.cs (4.74 kb)

Comments (1) -

  • Doug Telford

    3/5/2013 8:44:06 AM |

    Nice program.  Appears to need a unary minus.  I get errors that
    "the type Rational64 does not support the operator ~-

    Regards,
    Doug

Comments are closed