Exploring the generic math interfaces and fixed-point arithmetic in .NET

In .NET 7 (previewed in .NET 6) a series of new interfaces were introduced to make work with mathematical operations easier.

.NET has had generics almost forever allowing you to write algorithms that work with many different types, but this hasn’t been possible for basic maths until this was introduced.

The main reason this has been done now is for machine learning and AI implementations where the classic computing tradeoff of speed vs. space appears - if you’re dealing with very large arrays of numbers, maybe you want to switch from a 64 bit double to 32 bit float (or even the newly introduced 16 bit half).

The in-progress implementation of my expanded Elo rating system is based on this for that reason.

Fixed-point arithmetic

There is another reason to use the interfaces though - perhaps you want a completely different implementation of the basic mathematical operations. One example would be fixed-point arithmetic instead of floating-point arithmetic.

The biggest reason to do this is because floating-point arithmetic is not, in practice, deterministic.

More about floating-point arithmetic determinism

This is a subtle topic. Any specific IEE 754 floating point operation is deterministic for the same inputs. But those inputs include various settings that might change unexpectedly, and things like reordering of operations will give you different results due to rounding.

And it is even worse in .NET (ironically) because of its portable nature. Your code could be JIT compiled completely out of your control on many different processor architectures.

Here are some more resources about it:

There have been a few implementations of fixed-point arithmetic in .NET:

That last one is fairly recent (it targets .NET 6) and is MIT licensed, so I decided to see if I could modify it to support the generic math interfaces.

GamesWithGravitas.FixMath

The (still in-progress) result is available here in GitHub.

It takes the F32 and F64 types from FixPointCS and implements the following interfaces:

  • INumber
  • IBinaryNumber
  • ISignedNumber
  • IRootFunctions
  • ITrigonometricFunction

Most of the work is forwarding to the existing implementations. Some of the things that I had to actually write code for:

Formatting and parsing

There are a bunch of methods relating to converting to and from strings. My implementation uses double as an intermediate type. I guess these have the chance to not be deterministic but for the things I’d use it for it would not matter.

TryConvertFrom… and TryConvertTo…

These methods are used to convert between these types and others. They come in three versions: Checked, Truncating and Saturating. I have currently implemented all three to do the same thing.