问题
Based on this interesting question: Addition of int and uint and toying around with constant folding as mentioned in Nicholas Carey's answer, I've stumbled upon a seemingly inconsistent behavior of the compiler:
Consider the following code snippet:
int i = 1;
uint j = 2;
var k = i - j;
Here the compiler correctly resolves k
to long
. This particular behavior is well defined in the specifications as explained in the answers to the previously referred question.
What was surprising to me, is that the behavior changes when dealing with literal constants or constants in general. Reading Nicholas Carey's answer I realized that the behavior could be inconsistent so I checked and sure enough:
const int i = 1;
const uint j = 2;
var k = i - j; //Compile time error: The operation overflows at compile time in checked mode.
k = 1 - 2u; //Compile time error: The operation overflows at compile time in checked mode.
k
in this case is resolved to Uint32
.
Is there a reason for the behavior being different when dealing with constants or is this a small but unfortunate "bug" (lack of a better term) in the compiler?
回答1:
From the C# specification version 5, section 6.1.9, Constant Expressions only allow the following implicit conversions
6.1.9 Implicit constant expression conversions
An implicit constant expression conversion permits the following conversions:
* A constant-expression (§7.19) of typeint
can be converted to typesbyte
,byte
,short
,ushort
,uint
, orulong
, provided the value of the constant-expression is within the range of the destination type.
• A constant-expression of typelong
can be converted to typeulong
, provided the value of the constant-expression is not negative.
Note that long
is not on the list of int
conversions.
The other half the problem is that only a small number of numeric promotions happen for binary operations:
(From Section 7.3.6.2 Binary numeric promotions):
- If either operand is of type decimal, the other operand is converted to type decimal, or a binding-time error occurs if the other operand is of type float or double.
- Otherwise, if either operand is of type double, the other operand is converted to type double.
- Otherwise, if either operand is of type float, the other operand is converted to type float.
- Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or a binding-time error occurs if the other operand is of type sbyte, short, int, or long.
- Otherwise, if either operand is of type long, the other operand is converted to type long.
- Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
- Otherwise, if either operand is of type uint, the other operand is converted to type uint.
- Otherwise, both operands are converted to type int.
REMEMBER: The int
to long
conversion is forbidden for constants, meaning that both args are instead promoted to uint
s.
回答2:
Check out this answer here
The problem is that you are using const.
At run time when there is a const the behavior is exactly as with literals, or as if you had simply hard coded those numbers in the code, so since the numbers are 1 and 2 it casts to a Uint32 since 1 is within the range of uint32. Then when you try to subtract 1 - 2 with uint32 it overflows, since 1u - 2u = +4,294,967,295 (0xFFFFFFFF).
The compiler is allowed to look at litterals, and interpret them different than it would other variables. Since const will never change, it can make guarantees that it otherwise couldn't make. in this instance it can guarentee that 1 is within the range of a uint, therfore it can cast it implicitly. In normal circumstances(without the const) it cannot make that guarantee,
a signed int ranges from -2,147,483,648 (0x80000000) to +2,147,483,647 (0x7FFFFFFF).
an unsigned int ranges from 0 (0x00000000) to +4,294,967,295 (0xFFFFFFFF).
Moral of the story, be careful when mixing const and var, you may get something you don't expect.
来源:https://stackoverflow.com/questions/26386201/subtracting-uint-and-int-and-constant-folding