Bubble floating-point numbers in .Net do not overflow, since integer arithmetic can be performed.
They help go to Double.PositiveIfinity, Double.NegativeIfinity or (especially for cases when a mathematical operation becomes invalid Double.Nan)
note that this is also a bit more complicated due to the floating point behavior when encountering two numbers with very different accuracy.
Console.WriteLine(double.MaxValue); Console.WriteLine(double.MaxValue * 2); Console.WriteLine(double.MaxValue + 1); Console.WriteLine(double.MaxValue + double.MaxValue);
gives:
1.79769313486232E+308 Infinity 1.79769313486232E+308 Infinity
It's also unclear what you want your checkOverflow function to execute, just write what happened?
If all this works (I converted to int for you)
void Main() { int a, b; a = int.MaxValue; b = 1; // Check if the expression a+b would overflow, *without* the need to use // try/catch around the expression checkOverflow(() => {checked { return a+b; }}); } private static void checkOverflow(Func<int> exp) { try { exp(); } catch(OverflowException) { Console.WriteLine("overflow!"); } }
I have to add a reason why this works:
checked is not a lexical field in the sense of influencing variables. This is the area interpreted by the compiler, saying that all the code inside here that does integer arithmetic should generate overflow capture commands. no matter where the variables come from, only where exactly is the code defined.
I believe your mental model is something like:
checked
This is not how it is checked, it works, it is checked, it determines which commands are issued at compile time (some commands intercept an overflow, and some do not)
A validated block does NOT affect code defined outside of it. For example, simply using functions:
int Times2(int a) { return a * 2; } void TheresNoDifferenceHere() { checked { Times2(int.MaxValue); } Times2(int.MaxValue); }
Times2 function call resolved with
IL_0000: nop IL_0001: ldarg.1 IL_0002: ldc.i4.2 IL_0003: mul IL_0004: stloc.0 IL_0005: br.s IL_0007 IL_0007: ldloc.0 IL_0008: ret
If you used
int Times2(int a) { checked { return a * 2; } } IL_0000: nop IL_0001: nop IL_0002: ldarg.1 IL_0003: ldc.i4.2 IL_0004: mul.ovf IL_0005: stloc.0 IL_0006: br.s IL_0008 IL_0008: ldloc.0 IL_0009: ret
Note the difference in using mul and mul.ovf. Thus, two appeals to him cannot change him for verification or not after the fact. The checked block around the first call in the above example does not actually affect the received IL. There are no specific operations that matter to him.
So your initial idea was to define an arithmetic operation in one place (without checking), and then doing it at another point (just like calling a function), but the โcheckedโ command cannot affect code that it didn't surround at compile time.
lambdas allows work with expression trees or an anonymous delegate (it may be supported by the required synthetic classes for storing and supporting any closing variables). In both cases, the proven aspect of any part of them is fully defined where they are defined, and not where they are called.