It’s a very rare requirement, but sometimes in .NET you have to create your own primitive and make it behave as close as possible to a native CTS (common type system) type. “That shouldn’t be hard” would be your first thought, until you start considering all the scenarios in which it could be used. Continue reading “Creating your own primitive type”
I’m not really sure why Microsoft have never bothere with implementing a Fraction primitive in .NET. I’m sure there are plenty of uses as fraction allow to preserve the maximum possible precision. I have therefore decided to create my own implementation (albeit somewhat primitive at this stage) .
My implementation automatically simplifies the fraction, so if you we to create
new Function(6, 3) that would be simplified to 2. The Fraction struct implements all the arithmetic operators on itself and on Int64, float, double and decimal.
Internally the Fraction is represented as two Int64: Numerator and Denominator and is always simplified upon initialisation. I initially intended to have it as an option, however following profiling the cost of simplification is not that great and the benefits outweigh the performance drawbacks.
Fraction has explicit conversion to Int64 (although that is bound to lose precision), float, double and decimal. It supports comparison with Int64, float, double and decimal and even supports ++ and — operations.
So far I have provided more or less complete implementation with plenty of Unit Test. Now the hard word of optimising the performance begins!
Design of Fractions
Fraction is implemented as a struct (pretty obvious choice). It takes a numerator as the first argument and denominator, it then tries to simplify the fraction using the Euclidean algorithm, so if you were to specify 333/111 it would become 3.
The implementation supports all arithmetic operations with long, float, double and decimal and can also be converted to those type by either calling the corresponding methods or using explicit cast.
You can also create a function from either a long, float, double or decimal. Conversion from a long is quite trivial however conversion from a float, double or a decimal goes through a while loop and multiplies the floating point number until it has no decimal places. This method is relatively slow and therefore is not recommended.
Apart from that the Fraction behaves like a fist class citizen: you can compare a Fraction to any other number, divide, multiply, add, subtract, compare, increment decrement etc.
var oneThird = Fraction(1, 3); var reciprocal = oneThird.Reciprocal(); Console.WriteLine(oneThird * reciprocal) : "1" Console.WriteLine(++oneThird) : "4/3" - just like with an integer ++ adds 1 Console.WriteLine(oneThird * oneThird) : "1/9"
Please feel free to contribute to the codebase if you feel like it