.NET/Mono: IArithmetic on Primitives

I’m sure anyone who’s written generic algorithms in C# (or other .NET languages) before has been thinking “wouldn’t it be nice if we could restrict a generic to a type that supports arithmetic operations?” at least once. I’m sure they’ve had to write some sort of hideous duplicated code because the primitive types in the base class library don’t implement some sort of interface saying “hey, I can do math magic for you!”

I’m thinking:

public interface IArithmetic<T>
{
    T Add(T addend);
    T Subtract(T subtrahend);
    T Multiply(T multiplier);
    T Divide(T divisor);
}

Now, for some reason, even with the release of F#, Microsoft has not yet added such an interface. They would be extremely useful for writing generic algorithms, and F#’s automatic generalization could take great advantage of it. Furthermore, the JIT could have special knowledge about the interfaces implemented on the primitive types, so that it could inline Add/Subtract/Multiply/Divide as simple add/sub/mul/div opcodes.

But no, even in .NET 4.0, we don’t get this luxury, and I don’t see it coming in 5.0 either. Why Microsoft hasn’t fixed this major problem yet eludes me.

Anyway, I took it upon myself to write an example of how this could work. The commit + code resides in my fork of Mono, in the arithmetic-interfaces branch.

If you take a look at IArithmetic.cs, you’ll notice that we have two interfaces to deal with: IArithmetic and IFloatingPointArithmetic. The first one defines a set of common operations that you’d expect on any whole number. IFloatingPointArithmetic defines operations that you’d expect on any real number. Note that IFloatingPointArithmetic inherits IArithmetic.

(Note: Admittedly, some of the operations on IFloatingPointArithmetic should be moved to IArithmetic. I’ll probably do that later.)

Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, and UInt64 all implement IArithmetic, while Single, Double, and Decimal implement IFloatingPointArithmetic. One could even make BigInteger (in System.Numerics in .NET 4.0) implement IArithmetic too.

Anyway, with this in place, let’s write some code. One method that has a zillion overloads in .NET is LINQ’s Sum method. Let’s see if we can eliminate the need for these overloads:

public static T Sum<T>(this IEnumerable<T> source)
    where T : IArithmetic<T>
{
    if (source == null)
        throw new ArgumentNullException("source");

    T sum = default(T);

    foreach (var item in source)
        sum = sum.Add(item);

    return sum;
}

That’s a very boring demonstration, but it shows my point: We just eliminated 5 overloads (Int32, Int64, Single, Double, Decimal) that all had the same code, but for different types.

(Note: Actually, that’s not entirely true. For some reason, the Single overload stores the sum in a Double variable. Don’t ask me why. Seems pointless, considering the other methods use their respective type. Since there doesn’t seem to be any reasoning behind this, I’ve chosen to ignore it.)

Edit: As Joel Mueller points out, the reason is accuracy. OK, so this might be a fairly bad example then. :)

We can also implement a simple vector type (similar to the XNA API):

public struct Vector3<T>
    where T : IArithmetic<T>
{
    public readonly T X, Y, Z; // Immutable data types are nice.

    public Vector3(T x, T y, T z)
    {
        X = x;
        Y = y;
        Z = z;
    }

    public static Vector3<T> operator + (Vector3<T> vec1, Vector3<T> vec2)
    {
        var x = vec1.X.Add(vec2.X);
        var y = vec1.Y.Add(vec2.Y);
        var z = vec1.Z.Add(vec2.Z);
        return new Vector3<T>(x, y, z);
    }

    public static Vector3<T> operator / (Vector3<T> vec, T scalar)
    {
        var x = vec.X.Divide(scalar);
        var y = vec.Y.Divide(scalar);
        var z = vec.Z.Divide(scalar);
        return new Vector3<T>(x, y, z);
    }

    public static Vector3<T> operator / (Vector3<T> vec1, Vector3<T> vec2)
    {
        var x = vec1.X.Divide(vec2.X);
        var y = vec1.Y.Divide(vec2.Y);
        var z = vec1.Z.Divide(vec2.Z);
        return new Vector3<T>(x, y, z);
    }

    public static Vector3<T> operator * (Vector3<T> vec1, Vector3<T> vec2)
    {
        var x = vec1.X.Multiply(vec2.X);
        var y = vec1.Y.Multiply(vec2.Y);
        var z = vec1.Z.Multiply(vec2.Z);
        return new Vector3<T>(x, y, z);
    }

    public static Vector3<T> operator * (Vector3<T> vec, float scalar)
    {
        var x = vec1.X.Multiply(scalar);
        var y = vec1.Y.Multiply(scalar);
        var z = vec1.Z.Multiply(scalar);
        return new Vector3<T>(x, y, z);
    }

    public static Vector3<T> operator - (Vector3<T> vec1, Vector3<T> vec2)
    {
        var x = vec1.X.Subtract(vec2.X);
        var y = vec1.Y.Subtract(vec2.Y);
        var z = vec1.Z.Subtract(vec2.Z);
        return new Vector3<T>(x, y, z);
    }

    public static Vector3<T> operator - (Vector3<T> vec)
    {
        var x = vec1.X.Negate();
        var y = vec1.Y.Negate();
        var z = vec1.Z.Negate();
        return new Vector3<T>(x, y, z);
    }

    public T SquaredLength()
    {
        var xSq = X.Multiply(X);
        var ySq = Y.Multiply(Y);
        var zSq = Z.Multiply(Z);
        return xSq.Add(ySq.Add(zSq));
    }

    public T Length()
    {
        return SquaredLength().Sqrt();
    }

    public Vector3<T> Normalized()
    {
        var length = Length();
        var x = X.Divide(length);
        var y = Y.Divide(length);
        var z = Z.Divide(length);
        return new Vector3<T>(x, y, z);
    }

    public T Dot(Vector3<T> other)
    {
        var x = X.Multiply(other.X);
        var y = Y.Multiply(other.Y);
        var z = Z.Multiply(other.Z);
        return x.Add(y.Add(z));
    }

    public T SquaredDistance(Vector3<T> other)
    {
        var diff = other - this;
        return diff.SquaredLength();
    }

    public T Distance(Vector3<T> other)
    {
        var dist = SquaredDistance(other);
        return dist.Sqrt();
    }

    public Vector3<T> Cross(Vector3<T> other)
    {
        var x = Y.Multiply(other.Z).Subtract(Z.Multiply(other.Y));
        var y = Z.Multiply(other.X).Subtract(X.Multiply(other.Z));
        var z = X.Multiply(other.Y).Subtract(Y.Multiply(other.X));
        return new Vector3<T>(x, y, z);
    }

    public Vector3<T> Lerp(Vector3<T> other, float amount)
    {
        var x = X.Add(other.X.Subtract(X)).Multiply(amount);
        var y = Y.Add(other.Y.Subtract(Y)).Multiply(amount);
        var z = Z.Add(other.Z.Subtract(Z)).Multiply(amount);
        return new Vector3<T>(x, y, z);
    }
}

Now this is interesting. We’ve managed to make a generic vector that supports the basic arithmetic operators as well as vector operations like length, distance, dot product, cross product, and lerp! Now we can use this Vector3 struct with any primitive that supports basic arithmetic – Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, and theoretically BigInteger. Quite useful, I dare claim!

Note that the code committed in my fork will likely never reach upstream Mono. JB gives a good reason: Programs compiled against Mono that utilize these interfaces wouldn’t run under .NET. We can only hope that Microsoft will one day realize the extreme level of usefulness in this simple change.

(Last note: Some of the arithmetic implemented on Decimal has very high potential to overflow, as it rounds values to a Double. The reason is that I’m not quite sure how the Decimal internals work, and thus can’t implement certain operations correctly… Yet. Also, another thing is, the sine/cosine/tangent functions would be incredibly slow with full Decimal-level precision, as they would no longer be hardware-accelerated (see the fsin, fcos, and ftan x87 instructions).)

7 thoughts on “.NET/Mono: IArithmetic on Primitives

  1. “the JIT could have special knowledge about the interfaces implemented on the primitive types” – I’d be excited to see this. The JIT should already generate different machine code for Sum, Sum, Sum etc, as these are all value types. Arithmetic on non-primitive types (such as System.Decimal) goes via a static method anyway.

    What you’ve done is similar to the Haskell type classes that support arithmetic. Whereas adding full type class functionality to .NET sounds difficult (at least if you don’t want to use reflection in the generated code), special-casing arithmetic sounds easier.

  2. Pingback: Financial Modeling: Functions to Up Your Game : Cars Blog | Everything You should Know about Cars

  3. Just remember the boxing issues with this approach if the JIT isn’t aware of this interface. “the JIT could have special knowledge about the interfaces implemented on the primitive types” should really be …MUST… .

    Before you can do an interface call on a value type it must be boxed. That XNA example would kill the GC (to the point of being unusable on the XBox) if the JIT wasn’t coded around this.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s