2015-03-25 96 views
8

这真的很奇怪。我跟踪这个错误:不同的铸造行为

Negating the minimum value of a twos complement number is invalid.

...和事实证明,这是由于这样的代码:

var valueFromUser = "470259123000000"; 
var doubleValue = Convert.ToDouble(valueFromUser, CultureInfo.InvariantCulture); 
Math.Abs((int)doubleValue); 

事实上,当我在LINQPad运行此:

(int)Convert.ToDouble("470259123000000", CultureInfo.InvariantCulture) 

...它给了我:

-2147483648

然而,另一名开发人员在这里说,他得到完全不同的东西(而不是在LINQPad):

-1141206336

当我试图通过自身的恒定,以评估只是演员:

(int)470259123000000.0 

...我由于需要unchecked而出现编译错误。而这个:

unchecked((int)470259123000000.0) 

...评估为-1141206336像其他开发者得到。所以我想也许Convert创造了一个微妙的不同于常数的值。不,这个计算结果为True

Convert.ToDouble("470259123000000", CultureInfo.InvariantCulture) == 470259123000000.0 

到底是什么怎么回事?为什么评估这些看起来相同的表达式会产生不同的结果?

更新

找到一个提示。的4.70259123E14-1141206336十六进制表示是:

0x42FABB2BBFA92C00 
     0xBBFA92C0 

所以我想了铸件一种是直接推搡位到int。所以-2147483648是更大的谜。

+0

有趣。看起来像检查和未检查铸件行为之间的区别。使用高科技的“计算器”应用程序(当然在“程序员”模式;-)我可以看到'-1141206336'匹配'470259123000000'的截尾(32 lsb)。选中的版本似乎返回'int.MinValue'。我相信有人能够在语言规范中指出这一点。 – Alex 2015-03-25 03:02:39

回答

3

我不完全确定潜在的原因,但它看起来像一个编译器错误,因为用Roslyn编译的程序为这两个表达式提供相同的值(-2147483648)。

允许编译器在编译时计算常量表达式。所有带有未经检查的表达式的转换都由编译器完成,但在另一种情况下,它们是在CLR运行时完成的,因此总是有可能使用稍微不同的规则。正如您所看到的,编译器似乎截断与运行时不同的值,以将该值合并到32位整数中。您可以在底层IL中看到程序正在加载常量值(0xbbfa92c0)而不是未检查的表达式。

using System; 
using System.Globalization; 

public class Program 
{ 
    public static void Main(string[] args) 
    { 
     var n = unchecked((int)470259123000000.0); 
     Console.WriteLine(n); 

     n = (int)Convert.ToDouble("470259123000000", CultureInfo.InvariantCulture); 
     Console.WriteLine(n); 
    } 
} 

反编译IL从.NET 4.5编译器:

.method public hidebysig static void Main(string[] args) cil managed 
    { 
    // 
    .maxstack 2 
    .locals init (int32 V_0) 
    IL_0000: nop 
    IL_0001: ldc.i4  0xbbfa92c0 
    IL_0006: stloc.0 
    IL_0007: ldloc.0 
    IL_0008: call  void [mscorlib]System.Console::WriteLine(int32) 
    IL_000d: nop 
    IL_000e: ldstr  "470259123000000" 
    IL_0013: call  class [mscorlib]System.Globalization.CultureInfo [mscorlib]System.Globalization.CultureInfo::get_InvariantCulture() 
    IL_0018: call  float64 [mscorlib]System.Convert::ToDouble(string, 
                    class [mscorlib]System.IFormatProvider) 
    IL_001d: conv.i4 
    IL_001e: stloc.0 
    IL_001f: ldloc.0 
    IL_0020: call  void [mscorlib]System.Console::WriteLine(int32) 
    IL_0025: nop 
    IL_0026: ret 
    } // end of method Program::Main