views:

2319

answers:

3

Is there any feasible way of using generics to create a Math library that does not depend on the base type chosen to store data?

In other words, let's assume I want to write a Fraction class. The fraction can be represented by two ints or two doubles or whatnot. The important thing is that the basic four arithmetic operations are well defined. So, I would like to be able to write "Fraction<int> frac = new Fraction<int>(1,2)" and/or "Fraction<double> frac = new Fraction<double>(0.1, 1.0).

Unfortunately there is no interface representing the four basic operations (+,-×,÷). Has anybody found a workable, feasible way of implementing this?

A: 

First, your class should limit the generic parameter to primitives ( public class Fraction where T : struct, new() ).

Second, you'll probably need to create implicit cast overloads so you can handle casting from one type to another without the compiler crying.

Third, you can overload the four basic operators as well to make the interface more flexible when combining fractions of different types.

Lastly, you have to consider how you are handling arithmetic over and underflows. A good library is going to be extremely explicit in how it handles overflows; otherwise you cannot trust the outcome of operations of different fraction types.

Will
The problem is that I cannot even do sums like that because structs don't have an addition operator defined.
Sklivvz
http://msdn.microsoft.com/en-us/library/aa691324(VS.71).aspx "user-defined implementations can be introduced by including operator declarations in classes and structs"
Will
+2  A: 

I believe this answers your question:

http://www.codeproject.com/KB/cs/genericnumerics.aspx

That solution, and others available (like using Emit) are not really clean at all, so that's not what I was looking for. But thanks anyways :)
Sklivvz
+9  A: 

Here is a way to abstract out the operators that is relatively painless.

    abstract class MathProvider<T>
    {
        public abstract T Divide(T a, T b);
        public abstract T Multiply(T a, T b);
        public abstract T Add(T a, T b);
        public abstract T Negate(T a);
        public virtual T Subtract(T a, T b)
        {
            return Add(a, Negate(b));
        }
    }

    class DoubleMathProvider : MathProvider<double>
    {
        public override double Divide(double a, double b)
        {
            return a / b;
        }

        public override double Multiply(double a, double b)
        {
            return a * b;
        }

        public override double Add(double a, double b)
        {
            return a + b;
        }

        public override double Negate(double a)
        {
            return -a;
        }
    }

    class IntMathProvider : MathProvider<int>
    {
        public override int Divide(int a, int b)
        {
            return a / b;
        }

        public override int Multiply(int a, int b)
        {
            return a * b;
        }

        public override int Add(int a, int b)
        {
            return a + b;
        }

        public override int Negate(int a)
        {
            return -a;
        }
    }

    class Fraction<T>
    {
        static MathProvider<T> _math;
        // Notice this is a type constructor.  It gets run the first time a
        // variable of a specific type is declared for use.
        // Having _math static reduces overhead.
        static Fraction()
        {
            // This part of the code might be cleaner by once
            // using reflection and finding all the implementors of
            // MathProvider and assigning the instance by the one that
            // matches T.
            if (typeof(T) == typeof(double))
                _math = new DoubleMathProvider() as MathProvider<T>;
            else if (typeof(T) == typeof(int))
                _math = new IntMathProvider() as MathProvider<T>;
            // ... assign other options here.

            if (_math == null)
                throw new InvalidOperationException(
                    "Type " + typeof(T).ToString() + " is not supported by Fraction.");
        }

        // Immutable impementations are better.
        public T Numerator { get; private set; }
        public T Denominator { get; private set; }

        public Fraction(T numerator, T denominator)
        {
            Numerator = numerator;
            Denominator = denominator;
        }

        public static Fraction<T> operator +(Fraction<T> a, Fraction<T> b)
        {
            return new Fraction<T>(
                _math.Add(a.Numerator, b.Numerator),
                _math.Add(a.Denominator, b.Denominator));
        }

        public static Fraction<T> operator -(Fraction<T> a, Fraction<T> b)
        {
            return new Fraction<T>(
                _math.Subtract(a.Numerator, b.Numerator),
                _math.Subtract(a.Denominator, b.Denominator));
        }

        public static Fraction<T> operator /(Fraction<T> a, Fraction<T> b)
        {
            return new Fraction<T>(
                _math.Multiply(a.Numerator, b.Denominator),
                _math.Multiply(a.Denominator, b.Numerator));
        }

        // ... other operators would follow.
    }

If you fail to implement a type that you use, you will get a failure at runtime instead of at compile time (that is bad). The definition of the MathProvider<T> implementations is always going to be the same (also bad). I would suggest that you just avoid doing this in C# and use F# or some other language better suited to this level of abstraction.

fryguybob
Great answer, thank you!
Sklivvz
nice an clean :)
Lukas Šalkauskas
In the general way I think MathProvider should be made into a interface and Subtract into a ordinary interface method or it could be implemented as an Extension Method. That would on the other hand disallow overriding it.
dalle
did you just implement a/b - c/d as (a-c)/(b-d)?? gasp! ;)
Lucas