This is a very old and widely known numeric computation problem. You already said you are looking for a solution other than defining some margin of error to every comparison. An approach that comes to my mind is to build the mathematic expression tree in memory first and do the calculation last. Having this at hand we can do some simplifications using some known rules before doing the calculations. Rules such as:
- Remove equal numbers in numerator and denominator of a fraction if they are not zero
- Square root of a square of a number is the number itself
- ...
Therefore instead of storing 1/3 in a decimal/double value which equals 0.33333... we can store an instance of Fraction(1, 3)
. Then we can define all other expressions like this to build an expression rather than doing the calculations. In the end we can first simplify the expression with the rules above and then calculate the result.
I searched the web briefly to find libraries to do that and didn't find any yet. But I'm sure some can be found for other languages/platforms or even .NET.
Please note that the above approach in the end just makes a better result and yet does not solve this problem inherent in the nature of numeric computations.