3

I am having a hard time determining if a checked operation on an int using a Func<int, int> resulted in an overflow or underflow. If I do not use a Func<int, int> I have found a way to determine this

int _Data = Data; // Data is some integer value
int scaleValue = int.MaxValue; // Just for completion, could be any value
try 
{ 
    checked 
    { 
        Data *= scaleValue; 
    } 
}
catch (System.OverflowException ex)
{
    // Cast the values to float so we can operate beyond the integer range
    // This way we can check if we went over Max or Min Values
    if ((float)_Data * (float)scaleValue > int.MaxValue)
        Data = int.MaxValue;
    else
        Data = int.MinValue;
}

Though if I do use a Func<int, int> then I cannot use my trick above to determine the outcome.

int _Data = Data;
Func<int, int> scaleFunction = (x) => (x * int.MaxValue);
try 
{ 
    checked 
    { 
        Data = scaleFunction(Data); 
    } 
}
catch (System.OverflowException ex)
{
    // How to tell if scaleFunction created under or over flow?
    if ((float)_Data * (float)scaleValue > int.MaxValue)
        Data = int.MaxValue;
    else
        Data = int.MinValue;
}

The only option I see is to "alter" the given Func<int, int> to work on float, but if the structure of the Func<int, int> is not known before hand then I do not think it can be changed.

Can anyone see a sneaky way to accomplish this?

This is the thread where I found the original sneaky operation: Determine if integer overflow is over or under bounds

Here is a related question I asked earlier that gives some more background: Determine if operation will result in overflow?

EDIT: In my usage of this code I will not have control over the way that the Func<int, int> is created. Thus I cannot put a check expression or blocked inside of it.

I also did not realize that check does not check for overflow "inside functions" that are used within it. If this is so, I think that this problem is much harder. As a result of not being able to modify the Func to use check we have to do the check by hand.

The issue with doing it by hand is that it (to me currently) seems impossible. It might make sense to use the trick of checking whether the original value has the same sign as the output value from the Func. The issue is that you cannot tell if the Func increased the value past the max illegally, or if it decreased the value below 0 legally; or vise versa.

Community
  • 1
  • 1
KDecker
  • 6,928
  • 8
  • 40
  • 81
  • 2
    you should put checked inside lambda. like this `x => { checked { return x*int.MaxValue; } };` Can you change function? – M.kazem Akhgary Nov 17 '15 at 18:25
  • 2
    @M.kazemAkhgary You can even write that simpler by using a `checked` expression instead of a `checked` block: `x => checked(x * int.MaxValue)` – Wormbo Nov 17 '15 at 18:35
  • also when operation on int does not overflow it doesnt mean it underflows. although run time throws overflow exception in both cases. but you can assume when result is lesser than `int.MinValue` then its underflowed. you have to check for underflow too. instead of `else ....` you should do this `else if ((double)_Data * (double)scaleValue < int.MinValue) ....` – M.kazem Akhgary Nov 17 '15 at 18:59
  • @M.kazemAkhgary So a `check` block only throws an `OverflowException` if the operation overflows and does not throw one for underflow? – KDecker Nov 17 '15 at 19:22
  • 1
    It seems that an `OverflowException` is thrown in either case after testing. So if a checked block throws an exception there are only two cases over or under flow. Checking one case and finding it is false, must mean the other case is true. – KDecker Nov 17 '15 at 19:30
  • Hmm. Didnt noticed that. So you can throw overflow exceptions on int globally. Look at this answer and possibly duplicate. http://stackoverflow.com/a/29408056/4767498 – M.kazem Akhgary Nov 17 '15 at 19:47
  • My issue is outlined more clearly in an edit to the post above. Basically using a `Func` instead of a known operation it seems that there is no way to tell if an operation over or under flowed. Yes I could throw them globally though I do not want to do this for other reasons. – KDecker Nov 17 '15 at 19:50
  • Thats the only way (best way IMO) and You just have to change project settings. In that case the program throws overflow exception by default instead of ignoring it. Also you can use `unchecked` statement where you want to ignore these overflow exceptions again. – M.kazem Akhgary Nov 17 '15 at 19:53
  • Can you support using Func and truncating result into Int32.MinValue Int32.MaxValue range? – user629926 Nov 17 '15 at 20:06
  • No, the incoming `Func` will always have the parameters `` – KDecker Nov 17 '15 at 20:09

2 Answers2

4

In my usage of this code I will not have control over the way that the Func is created. Thus I cannot put a check expression or blocked inside of it.

Then, you have lost.

checked/unchecked is a property of any individual arithmetic instruction at the IL level. It is not flowed across method calls. It is not a setting that you can switch on or off. It is compiled into the .NET binary for each instruction individually.

The author of any piece of code decides the checked property and it cannot be altered.

In fact it would be dangerous and unreliable if you could reach into other peoples code and subtly alter the behavior of basic arithmetic instructions.

usr
  • 168,620
  • 35
  • 240
  • 369
1

checked blocks or expressions (both flavors exist and have identical effects) are not "inherited" by called methods, so your lambda is actually multiplying in unchecked context.

Try the following instead:

int _Data = Data;
Func<int, int> scaleFunction = (x) => checked(x * int.MaxValue);
try 
{ 
    Data = scaleFunction(Data); 
} 
catch (System.OverflowException ex)
{
    // handle overflow...
}
Wormbo
  • 4,978
  • 2
  • 21
  • 41
  • 1
    The issue is that at that point in the code I will not be the one who has created the function. I ultimately have to accept any `Func` given to me and as a result make sure the usage of such does not over/under flow my `Data`. – KDecker Nov 17 '15 at 19:24
  • @KDecker In that case usr's answer is the only correct one. Overflowing without exception is an intentional part of many calculations, so it's not controllable from the outside at runtime. Note that C# allows specifying the default (compile time) overflowing behavior in the project settings under Build -> Advanced. Implementers are of course still free to override that behavior using `unchecked` blocks or expressions. – Wormbo Nov 18 '15 at 05:10