2015-03-25 17 views
8

Das ist wirklich komisch. Ich war das Aufspüren von diesen Fehler:Unterschiedliches Gießverhalten

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

... und es stellte sich heraus, dass es zu Code wie folgt fällig war:

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

der Tat, wenn ich dies in LINQPad laufen:

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

... es gibt mir:

-2147483648

jedoch ein anderer Entwickler hier, sagt er etwas ganz anderes (nicht in LINQPad) bekommt:

-1141206336

Wenn ich versuche, auf einem konstanten selbst nur die Besetzung zu bewerten:

(int)470259123000000.0 

... I Erhalte einen Kompilierfehler, da unchecked benötigt wird. Und dies:

unchecked((int)470259123000000.0) 

... ausgewertet -1141206336 wie der andere Entwickler bekam. Also dachte ich, vielleicht hat der Convert einen subtil anderen Wert als die Konstante erstellt. Nein, diese auswertet zu True:

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

Was zum Teufel ist hier los? Warum führt die Auswertung dieser scheinbar identischen Ausdrücke zu so unterschiedlichen Ergebnissen?

aktualisieren:

einen Hinweis gefunden. Die hexadezimale Darstellung von 4.70259123E14 und -1141206336 ist:

0x42FABB2BBFA92C00 
     0xBBFA92C0 

Also ich denke, einer der Abgüsse wird die Bits direkt in den int schubsen. So ist -2147483648 das größere Geheimnis.

+0

Interessant. Scheint wie ein Unterschied zwischen überprüftem und ungeprüftem Casting-Verhalten.Mit der High-Tech-Anwendung "Calculator" (natürlich im "Programmer" -Modus ;-) kann ich sehen, dass '-1141206336' dem abgeschnittenen (32 lsb) von' 470259123000000' entspricht. Die geprüfte Version scheint "int.MinValue" zurückzugeben. Ich bin sicher, dass jemand in der Sprachspezifikation darauf hinweisen kann. – Alex

Antwort

3

Ich bin mir nicht ganz sicher über den zugrunde liegenden Grund, aber es sieht aus wie ein Compiler-Fehler, weil ein mit Roslyn kompiliertes Programm den gleichen Wert (-2147483648) für beide Ausdrücke gibt.

Der Compiler darf konstante Ausdrücke zur Kompilierzeit auswerten. Alle Konvertierungen mit dem ungeprüften Ausdruck werden vom Compiler ausgeführt, im anderen Fall werden sie zur Laufzeit von der CLR ausgeführt, so dass immer die Möglichkeit besteht, dass sie leicht unterschiedliche Regeln verwenden. Wie Sie gesehen haben, scheint der Compiler anders abgeschnitten zu haben als die Laufzeit, um den Wert in eine 32-Bit-Ganzzahl zu bringen. Sie können in der zugrunde liegenden IL sehen, dass das Programm nur den konstanten Wert (0xbfa92c0) anstelle des ungeprüften Ausdrucks lädt.

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); 
    } 
} 

dekompilierte IL von .NET 4.5 Compiler:

.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 
Verwandte Themen