C# – Which is preferred: subclass double or create extension methods to test (relative) equality due to floating point differences

cdesignextension-methodfloating pointsubclassing

I am writing numerical calculation software using .NET C#, which needs to be blazingly fast. There is a lot of fractional math. So using decimal type is pretty much out of the question, given its poor speed relative to using double. But of course double has its problems testing for equality, with floating point rounding issues.

My options seem to be subclassing double and overriding ==, < and >; versus creating extension methods for double equivalent to these. My tendency is to go with the latter – less code to change and maybe it will be less confusing to others reading the code later? Is there another option? What are other good reasons to choose one over the other?

Best Answer

"double has its problems testing for equality".

No, that is not true. "double" does not have such problems. Equality testing for double values is well defined and usually works as it should (which may sometimes not be what several programmers expect, of course).

Truth is: programmers have often problems with testing for equality correctly in numerical software. You cannot simply fix this by using another data type, or by providing some standard equality comparers with some standard precision for equality up-front. Though such approaches may be part of a solution, you first and foremost need to make sure the programmers in your team know how to do floating point comparisons correctly.

Before reading the rest of my answer, please have a look into "What Every Computer Scientist Should Know About Floating-Point Arithmetic". Now. No excuses.

So, since you read this paper, you now have learned that there are several alternatives on how comparisons can be done when using floating point numbers, and one has to pick the correct one for the specific case. For example, it may be necessary to take absolute or relative errors into account, to analyse the required precision for each individual comparison/quantity, or to take the specific operations and algorithms into account which will be used in the numerical software you are designing. Another thing which might be necessary is to adapt the scaling of some quantities, or other measures to keep rounding errors under control.

To find out what one really needs, I would recommend starting to implement some of the algorithms and determine precisely which kind of floating point comparisons are required there. When comparisons of the same kind occur more than two or three times, then it is time to refactor them into a reusable library (maybe using extension methods, which is a useful way in C# whenever it comes to adding some reuseable methods to an existing type one cannot change). It should be clear now why overloading an operator like == is not useful, since there is only one such operator per type, with no additional parameters like a precision.

Don't try this up-front until you have already written several of such numerical programs!