I wanted to share a pattern I am developing to make working with scientific measurements easier in C#.
Usually when people code scientific topics, they use "double" for all their values. This means positions, accelerations, velocities, etc are all stored as numeric scalars that can be added to each other (causing bugs) and you have no idea what units the values are in without digging all the way back to the initial assignment of the variables.
How many times have you seen someone pass in a degrees angle measurement into the system trig functions (which generally take an angle measurement in radians)? This will not throw an exception. It will result in bad computations that are very hard to detect in applications (you usually have to manually debug and watch the mathematics).
What can we do to help with scientific calculations in code?
- We can use the type-safeness to prevent the miss-use of measurements.
- We can track the units that each value is in to perform automatic unit conversions.
Here is the current version of the "Angle" type in my project:
The possible units of measurement are defined in an enum and the value of the measurement is a generic type so you can use whatever type you want: double, float, decimal, BigRational, etc. The conversion factors between the units are implemented as attributes on the enum values and are cached in a static conversion table for fast look up.
But what does the actual usage of the code look like? Here is a usage example:
When you define an angle, you provide the units that the measurement is in. From then on, all unit conversion necessary during mathematics will be automatically handled. In this case, we added a Degrees measurement to a Revolutions measurement, which automatically converted the Revolutions measurement into Degrees [0.5 Revolutions = 180 Degrees] for a result of [90 + 180 = 270 Degrees].
The other measurement types can be implemented as their own types: Length, Speed, Acceleration, Force, etc. This will give you type-safeness so that you cannot misuse the measurements. For example:
If we try to add a Length to an Angle the code will not compile. This is a nice extra layer of validity for your code (helps prevent bugs).
What about operations between measurement types that are valid? Easy, there are operators defined between specific types. This example shows how we can create a Speed value either directly or by dividing a Length value by a Time value:
Velocity is different from speed. Velocity is defined to include the direction as well at the magnitude. If you want to represent a velocity, you can just use Speed inside of a Vector. Here is an example:
C# is a type safe language... How am I doing mathematics on generic types? Runtime compilation! Runtime compilation with generic types allows you to break type safeness and assume that the necessary members exist. Example:
This only scratches the surface of the code. If you want to learn more about how it is implemented and/or try it for yourself please check out the project (it is open source): https://github.com/ZacharyPatten/Towel
Thanks. Hope you learned something.