# Generic math in .NET

# Before .NET 7

Some *.NET* languages like *C#* support the definition of arithmetic operators. These can be used to write math expressions using the familiar arithmetic operators (+, -, *, /) instead of calling methods (Add, Subtract, Multiply, Divide).

For math operations that are not expressed as operators, like trigonometric operations, *.NET* provides the *System.Math* static class where most methods only handle double precision (*double*) values. There are scenarios where using single precision (*float*) may be better so since the release of *.NET Core 2* there’s also a *System.MathF*.

Any type can define its custom arithmetic operators but it was hard to define them using generics because these only allow calling known methods. Operators are static methods and they could not be defined in an interface. Defining arithmetic operators for generic types like *Vector<T>* was possible but very cumbersome and hard to maintain. For this reason, most libraries define *Vector* types only for one numerical type.

# .NET 7 and beyond!

*.NET 7* with *C# 11 *brings several new features breaking the previous limitations. It is now possible to declare static virtual methods in interfaces which include the arithmetic operators.

The namespace *System.Numerics* now includes many interfaces that declare sets of mathematical operations defined by the native numerical .NET types. For example, a type that implements *IAdditionOperators<TSelf, TOther, TResult>*, has the *operator +* implemented.

Now you can implement the following generic *Sum* method:

`static T Sum<T>(IEnumerable<T> source)`

where T: IAdditiveIdentity<T, T>, IAdditionOperators<T, T, T>

{

var sum = T.AdditiveIdentity; // initialize to zero

foreach(var value in source)

sum += value; // add value to sum

return sum;

}

This method can be used to sum a collection of any type that defines the additive identity (zero) and the *operator +* (used by the +=). This includes all .NET native numerical types and can also include other types like vectors, quaternions, matrices, and many more.

NOTE:You can restrictTtoINumber<T>, allowing any numeric native type to be used, or you could restrict toIFloatingPoint<T>, allowing any floating point native type to be used. Yet, leaving the method like it is, allows it to be used by types that don’t implement all the interfaces required byINumber<TSelf>orIFloatingPoint<TSelf>. These are the bear minimums required and will support several more types.

*System.Numerics* includes new interfaces for math operations other than arithmetic operators, for example *ITrigonometricFunctions<TSelf>**. *This interface is implemented by some of the native numerical types in .NET. You can now write the following:

`// half-precision floating-point`

var sinHalf = Half.Sin(Half.Pi);

var arcSinHalf = Half.Asin(sinHalf);

// single-precision floating-point

var sinFloat = float.Sin(float.Pi);

var arcSinFloat = float.Asin(sinFloat);

// double-precision floating-point

var sinDouble = double.Sin(double.Pi);

var arcSinDouble = double.Asin(sinDouble);

Notice that the *Pi* constant, the *Sin* and *Asin* methods are defined for the floating-point types *Half*, *float* and *double*. This means that you no longer need or should even use either *System.Math* or *System.MathF*. Also, more numeric types are now supported.

*System.Numerics* provides several more similar interfaces: *IExponentialFunctions<TSelf>*, *IHyperbolicFunctions<TSelf>*, *ILogarithmicFunctions<TSelf>*, *IPowerFunctions<TSelf>*, *IRootFunctions<TSelf>*, and more.

The constants *E*, *Pi*, and *Tau* are defined in the interface *IFloatingPointConstants<TSelf>*.

# Conclusions

It is now possible to use generics in many more use cases. This makes code much cleaner and easier to maintain.

You should stop using *System.Math* or *System.MathF* and take advantage of the math provided by each type.

If you implement your custom numeric type, you should implement the interfaces provided in *System.Numerics*. This way, your type can be used in third-party developed methods.

I’ve been developing an open-source library that uses .NET generic math to implement primitives in several coordinate system: cartesian 2D and 3D, polar, spherical, and geodetic. It implements them as immutable value types, using generics to avoid unit mismatch execution errors.

Check it out at https://netfabric.github.io/NetFabric.Numerics/