减去uint和int以及常量折叠

时间:2014-10-15 15:21:36

标签: c# constantfolding

基于这个有趣的问题:Addition of int and uintNicholas Carey's answer中提及的常量折叠,我偶然发现了一个看似错误的问题编译器的行为不一致:

请考虑以下代码段:

int i = 1;
uint j = 2;
var k = i - j;

此处编译器正确地将k解析为long。这种特殊行为在规范中有明确定义,如前面提到的问题的答案所述。

令我感到惊讶的是,在处理文字常量常量时,行为会发生变化。阅读Nicholas Carey的answer我意识到这种行为可能不一致所以我检查并确定:

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已解析为Uint32

在处理常量时,是否存在行为不同的原因,或者这是一个小但不幸的错误" (编译器中没有更好的术语)?

2 个答案:

答案 0 :(得分:4)

C# specification version 5,第6.1.9节,常量表达式只允许以下隐式转换

  

6.1.9隐式常量表达式转换
  隐式常量表达式转换允许以下转换:
  * int类型的常量表达式(第7.19节)可以转换为sbytebyteshortushortuint类型,或ulong,前提是constant-expression的值在目标类型的范围内   •如果constant-expression的值不是负数,则long类型的常量表达式可以转换为ulong类型。

请注意,long转化列表中不包含int

问题的另一半是二进制操作只发生少量数字促销:

(来自第7.3.6.2节二进制数字促销):

  
      
  • 如果任一操作数的类型为十进制,则另一个操作数将转换为十进制类型,或者如果另一个操作数的类型为float或double,则会发生绑定时错误。
  •   
  • 否则,如果任一操作数的类型为double,则另一个操作数将转换为double类型。
  •   
  • 否则,如果任一操作数的类型为float,则另一个操作数将转换为float类型。
  •   
  • 否则,如果任一操作数的类型为ulong,则另一个操作数将转换为ulong类型,如果另一个操作数的类型为sbyte,short,int或long,则会发生绑定时错误。
  •   
  • 否则,如果任一操作数的类型为long,则另一个操作数将转换为long类型。
  •   
  • 否则,如果任一操作数的类型为uint而另一个操作数的类型为sbyte,short或int,则两个操作数都将转换为long类型。
  •   
  • 否则,如果任一操作数的类型为uint,则另一个操作数将转换为uint类型。
  •   
  • 否则,两个操作数都将转换为int。
  •   

请记住:常量禁止intlong转换,这意味着两个参数都会被提升为uint s。

答案 1 :(得分:3)

查看此答案here

问题是你使用的是const。

在存在const的运行时,行为与文字完全相同,或者就好像您只是在代码中对这些数字进行了硬编码一样,因此数字为1和2,因为1在1内,所以它会转换为Uint32 uint32的范围。然后当你试图用uint32减去1 - 2时它会溢出,因为1u - 2u = +4,294,967,295(0xFFFFFFFF)。

允许编译器查看litterals,并解释它们与其他变量不同。因为const永远不会改变,所以它可以保证它无法做到。在这种情况下,它可以保证1在一个uint的范围内,因此它可以隐含地投射它。在正常情况下(没有const)它无法做出保证,

signed int的范围是-2,147,483,648(0x80000000)到+2,147,483,647(0x7FFFFFFF)。

unsigned int的范围是0(0x00000000)到+4,294,967,295(0xFFFFFFFF)。

故事的道德,混合const和var时要小心,你可能得到一些你不期望的东西。