2015-03-25 144 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