Search code examples
c#.net.net-corefloating-point-exceptions

(C#) Should I Guard Against Infinity/-Infinity in my calculations? I.e. Throw an Overflow Exception?


Looking for some advice regarding Infinity/-Infinity in C#.

I'm currently building Geometry classes, for shapes like Rectangle/Circle etc. The user will provide the input for Width/Depth and Diameter respectively.

These properties will be double and I understand that instead of explicitly overflowing, if you were to multiple double.MaxValue by 2, then you would get +infinity etc.

Each of the shape classes will have additional properties such as Area, Perimeter etc. Therefore, even if the provided dimensions are less than MaxValue, there is a chance that computed values could be a huge number if the user was so inclined:

E.g. Math.PI x Math.Pow(diameter, 2) / 4 => Math.PI x Math.Pow(double.MaxValue, 2) / 4

(i.e. This method would result in +infinity even though the user provided MaxValue as input.)

My question is whether I should always be guarding against infinity? Should I throw an exception (OverflowException) if either the user value or the computed values enter infinity?

It looks like it could be a code smell to check for infinity for every property/method in these geometric classes... Is there a better way?

public double Area
{
    get
    {
        double value = Math.PI * Math.Pow(this.diameter, 2) / 4;
        if (double.IsInfinity(value)
        {
            throw new OverflowException("Area has overflowed.");
        }
    }
}

public double Perimeter
{
    get
    {
        double value = Math.PI * this.diameter;
        if (double.IsInfinity(value)
        {
            throw new OverflowException("Perimeter has overflowed.");
        }
    }
}

Appreciate your time and thoughts! Cheers.


Solution

  • Conceptually, you have a design problem. Values that result in an infinite area result in an invalid state for your shape.

    For example, say the shape in question is a circle. Any value passed in which results in Area > double.MaxValue is an invalid state. Stated differently, any value diameter where Math.PI * Math.Pow(diamater, 2) > double.MaxValue is invalid.

    Solve double.MaxValue = Math.PI * Math.Pow(diamater, 2) for diameter (it's 1.5129091144565236E+154, and you're welcome to validate my math), and check for that in your constructor,

    private const double MaxDiameter = 1.5129091144565236E+154;
    Circle(double diameter) 
    {
        if (diameter > MaxDiameter) 
        {
            throw ArgumentOutOfRangeException(); 
        }
    }
    

    --

    None of the above probably matters

    In practice, consider the following. the diameter of the known universe is around 30 gigaparsecs, which converted to Planck lengths (the smallest distance unit I can think of) is around 5.7e61, which is far, far below double.MaxValue.

    I'd not worry about double overflow in your properties. It's sensible to design your shape so you it uses a unit type you've defined, so you always know what units you're working with.

    public Circle(Meters diamater)
    public Rectangle(Meters width, Meters height)