I'm tweaking some code in a RationalNumber
implementation. In particular, inside the equality logic, I'm considering the following:
public bool Equals(RationalNumber other)
{
if (RationalNumber.IsInfinity(this) ||
RationalNumber.IsInfinity(other) ||
RationalNumber.IsNaN(this) ||
RationalNumber.IsNaN(other))
{
return false;
}
try
{
checked
{
return this.numerator * other.Denominator == this.Denominator * other.numerator;
}
}
catch (OverflowException)
{
var thisReduced = RationalNumber.GetReducedForm(this);
var otherReduced = RationalNumber.GetReducedForm(other);
return (thisReduced.numerator == otherReduced.numerator) && (thisReduced.Denominator == otherReduced.Denominator);
}
}
As you can see I'm using exceptions as a flow control mechanism. The reasoning behind this is that I do not want to incurr in the penalty of evaluating the greatest common divisor of both fractions on every equality check. Thus I only decide to do it in the least probable case: one or both cross products overflow.
Is this an acceptable practice? I've always read that exceptions should never be used as a flow mechanism of your code, but I don't really see another way to achieve what I want.
Any alternative approaches are welcome.
The reasoning behind this is that I do not want to incur in the penalty of evaluating the greatest common divisor of both fractions on every equality check.
This is sound reasoning. The total cost of this code is
{probability of fast-path} * {fast-path cost}
+ ((1.0 - {probability of fast-path}) * {slow-path cost})
Depending on the three constants involved this will be a good or bad choice. You need to have a good understanding of that data will be processed in practice.
Note, that exceptions are very slow. I once benchmarked them to be 10000 per second per CPU core and I'm not sure they would scale to multiple cores due to internal CLR locks involved.
Maybe you can add runtime profiling. Track the rate of exceptions. If too hight, switch off the optimization.
You probably should document why you did this.
It's also not an architectural problem because in case you change your mind later you can easily switch to a different algorithm.
As an alternative, you could first compute and compare unchecked. If the result is "not equal" it is guaranteed that the exact result would be "not equal", too. Even if overflow occurred. So that could be an exception free fast path if many numbers turn out to be not equal.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With