C# Math.Max type handling

791 Views Asked by At

I was writing a piece of code that prevents an ushort from overflow/underflow'ing. Before that I try:

ushort a = 0; //ushort.MinValue
a -= 1; //returns 65535 (ushort.MaxValue)

Then I write the real code, and I expect this to "fail" (not able to detect underflow):

ushort oldValue, rate, delta;
ushort newValue = Math.Max(ushort.MinValue, oldValue - rate * delta);

Interestingly an error (CS0266) prevents me from building it. While I expect that C# will use the ushort Max(ushort, ushort) overload, it is using int Max(int, int) overload and that ushort values are converted into int automatically. Of course when I explicit cast the result to ushort this works fine.

This makes me think, is C# detecting that a possible underflow can occur, so it's using int to do the comparison for me?

4

There are 4 best solutions below

0
On BEST ANSWER

This is because oldValue - rate * delta's result is of type int. This is because the "minimum data type" for which operators +, -, * and / are defined is int. So byte, ushort, etc. are converted to int first. Then, the result is again an int. So you need to cast explicitly to ushort.

Or you could use Convert.ToUInt16 which will raise an exception if an overflow occurs.

0
On

C# promotes (ushort * ushort) to int and takes the result of the subsequent (ushort - int) operation also as int. Finally, overload resolution picks Math.Max(int, int) for the Max(ushort, int) call by promoting the first ushort argument to int.

Now the problem you have (in addition to all the uninitialized variables) is that the result of Max(int, int) is an int, which has no implicit conversion to ushort.

For more info, you can read the following sections of the spec:

  • 7.3.6.2 Binary numeric promotions ("Otherwise, both operands are converted to type int.")
  • 6.1.2 Implicit numeric conversions
  • 7.5.3 Overload resolution
0
On

According to this MSDN page, the result type of calculations with ushorts is int. So what you need to do is cast the result of the calculation

oldValue - rate * delta

back to ushort:

(ushort)(oldValue - rate * delta)
0
On

Multiplications of smaller data types than int yield an int as result.

Therefor oldValue - rate * delta results in an int and the best overload of Math.Max(short.MinValue, int) is:

Math.Max Method (Int32, Int32)

(since an ushort/short can be widened to int implicitely, without conversion)