2

Using C#, I have a few custom classes where I need to be able to detect integer overflows and return a default minimum or maximum value depending on if the overflow was due to the result being over the maximum value or under the minimum value. I can't seem to find a suggestion on how to detect the "type" of overflow that occurs anywhere.

The classes are divided between two general types: ones that use signed values, and ones that use unsigned values.

As an example, here is one of the classes that deals with Int32 values:

public class Stat32Tf : IStat32T<float>
{
    #region fields

    private int baseValue, baseAdjustment;
    private float baseMultiplier;

    #endregion

    #region ctors

    public Stat32Tf()
    {
        baseValue = 0;
        baseAdjustment = 0;
        baseMultiplier = 1f;
    }

    public Stat32Tf(int baseValue, int baseAdjustment = 0, float baseMultiplier = 1f)
    {
        this.baseValue = baseValue;
        this.baseAdjustment = baseAdjustment;
        this.baseMultiplier = baseMultiplier;
    }

    #endregion

    #region properties

    public int BaseValue
    {
        get 
        { 
            return baseValue; 
        }
        set 
        {
            baseValue = value; 
        }
    }

    public int BaseAdjustment
    {
        get 
        { 
            return baseAdjustment; 
        }
        set 
        { 
            baseAdjustment = value; 
        }
    }

    public float BaseMultiplier
    {
        get 
        { 
            return BaseMultiplier; 
        }
        set 
        { 
            baseMultiplier = value; 
        }
    }

    public int TruncValue
    {
        get 
        { 
            return (int)Value; 
        }
    }

    public float Value
    {
        get 
        { 
            return (baseValue + baseAdjustment) * baseMultiplier; 
        }
    }

    #endregion

}

As you can see, the idea of the class is to hold a base value, an adjustment value, and a multiplier value, and return the aggregate value in the Value property. (The TruncValue property just, as it suggests, returns the truncated whole value, dropping any fractional values).

The goal is to handle overflows in the "get" accessor of the Value property and, if the result is over the max int value, return int.MaxValue and if it is under the min value, return int.MinValue, all without throwing the actual overflow error. The part that's making it tricky for me is that the adjustment values and multipliers could be negative values as well (as per the design requirement).

What is a safe way to achieve this? I have not been able to find any resources that address this kind of situation. I'm guessing some sort of arithmetic algorithm will need to be used to determine of results will be over or under.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mike Johnson
  • 686
  • 5
  • 13
  • if there is an overflow and the result is positive then the overflow was going under. If it turns out negative, the overflow was going over – Julián Urbano Apr 11 '13 at 12:58
  • @caerolus With multiplication involved, it's not so easy, the result can still have the expected sign: `123456*987654 = 1672727936` for 32-bit (two's complement if signed) integers with wrap-around overflow behaviour. – Daniel Fischer Apr 11 '13 at 13:01
  • Just FYI, integer overflows in C# do not throw an OverflowException by default; only in a `checked` environment. – John Willemse Apr 11 '13 at 13:03
  • @DanielFischer you're very right, I was referring just to the addition. Maybe computing the whole thing as `double`, checking overflow and then casting back to `float`? – Julián Urbano Apr 11 '13 at 13:04
  • @John Willemse Yea, the dev environment is running checked for debugging purposes but for this it's trivial since we don't actually want the overflow exception thrown anyway. We just need to be able to determine if a result "would" have overflowed and then determine which direction the overflow was so an appropriate default value can be returned. – Mike Johnson Apr 11 '13 at 13:06
  • In that case using a `double` to check if it's out of bounds of a `float`'s limits is a good solution I think. – John Willemse Apr 11 '13 at 13:09

2 Answers2

3

There are only a limited number of cases where it could underflow:

  • If baseValue and baseAdjustment are both negative -> If Int.MinValue - baseAdjustment > baseValue then you have an underflow.

  • If baseValue + baseAjustment is negative and baseMultiplier is positive -> If an overflow exception is raised, then it can only be an underflow.

  • If baseValue + baseAdjustment is positive but baseMultiplier is negative -> If an overflow exception is raised, then it can only be an underflow.

If you want to avoid raising/catching exception, then it might be a bit more complicated (you may want to cast the result as long and compare it against Int.MaxValue; that way it'll only raise an exception if the result goes over Long.MaxValue).

Lâm Tran Duy
  • 804
  • 4
  • 8
  • This problem has reminded me that my algebraic skills have somewhat faded haha. Your suggestions help clarify what my algorithm will need to check for. We don't want to raise the exception but I think using the suggestions you gave, I might be able to figure an algorithm to check operands before applied. – Mike Johnson Apr 11 '13 at 13:33
  • 1
    If you want to check if a multiplication of two positive numbers (let's say a and b) is going to overflow, you need to find out the maximum value for b: take the distance between Int.MaxValue and a, divide it by a and then truncate the number. If b is greater than the resulting value, then it will overflow. – Lâm Tran Duy Apr 11 '13 at 13:50
  • After checking, I noticed that you need to add 1 to the truncated number to get the correct value. – Lâm Tran Duy Apr 11 '13 at 14:11
  • I went with a solution close to that mentioned above as it simplified matters and, for now, seems to not impose any bugs or conflicts, but I would like to thank you for your answer Lam. If I ended up going with an arithmetic algorithm to approach this, I most certainly would have based it on your answer. If I could choose both answers as correct on StackOverflow I would like to include yours as well. – Mike Johnson Apr 11 '13 at 15:18
0

Floats are pretty big. Are you expecting the get value to overflow or do you expect the cast to int to overflow? If it's just the cast something similar to the following code might work.

//This answer is wrong, see below.
public int TruncValue
{
    get
    {
        if (Value > (float)int.MaxValue)
        {
            return int.MaxValue
        }
        else if (Value < (float)int.MinValue)
        {
            return int.MinValue
        }
        else
        {
            return (int)Value;
        }
    }
}

Although you might need some additional handling for the edge cases.

Edit - I played around with this in some code and found some behavior that I didn't expect, but apparently it is in the specification.

For example,

var Value = int.MaxValue + int.MaxValue //Ends up returning -2 with no exception in debug mode.
var MaxCalculatedValue = (int.MaxValue + int.MaxValue) * float.MaxValue //Ends up returning something like -3.4... ^38.

You really might need to up cast everything into a double and then check to see if the result is greater than or less than an int.

So it might look something like this:

public float Value
{
    get
    {
        var result = ((double)baseValue + (double)baseAdjustment) * (double)baseMultiplier;
        if (result > (double)int.MaxValue)
        {
           return (float)int.MaxValue)
        }
        if (result < (double)int.MinValue)
        {
           return (float)int.MinValue)
        }
        return (float)result;
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
cgotberg
  • 2,045
  • 1
  • 18
  • 18
  • "The goal is to handle overflows in the "get" accessor of the Value property" – Julián Urbano Apr 11 '13 at 13:15
  • Yea it's the int value I'm worried about overflowing but the float result still needs to be within the allowed int ranges as well. I'll try something like this out in the Value prop and see if it works as intended. – Mike Johnson Apr 11 '13 at 13:27
  • 1
    Updated answer. I made an assumption about behavior without testing it out and it looks like it got me into trouble. At least I learned something. – cgotberg Apr 11 '13 at 13:54
  • I had to modify the approach slightly but this is very close to the solution I went with. I didn't need to upcast the values into larger datatypes because the floating point types provided more than enough room for the possible max and min values. However, I had to create some constant values to represent the casted min and max values. The performance with runtime values as max and mins was horrendous, but as constant values it was surprisingly fast. Thank you all for the help. – Mike Johnson Apr 11 '13 at 15:15