2015-03-25 27 views
8

To jest naprawdę dziwne. Byłem tropienia ten błąd:Odmienne zachowanie podczas odlewania

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

... i okazało się, że to ze względu na kod tak:

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

Rzeczywiście, kiedy biegnę to w LINQPad:

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

... to daje mi:

-2147483648

Jednakże, inny deweloper tutaj mówi, że robi się coś zupełnie innego (nie w LINQPad):

-1141206336

Kiedy próbuję oceniać tylko do obsady sam na Constant:

(int)470259123000000.0 

... ja uzyskać błąd kompilacji z powodu konieczności unchecked. I tak:

unchecked((int)470259123000000.0) 

... ocenia się -1141206336 jak drugi deweloper dostaje. Pomyślałem więc, że może Convert stworzył subtelnie inną wartość niż stała. Nie, to ocenia się True:

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

Co do cholery się tu dzieje? Dlaczego ocena tych pozornie identycznych wyrażeń daje tak różne wyniki?

Aktualizacja:

Znaleziony podpowiedź. Reprezentacja hex 4.70259123E14 i -1141206336 jest:

0x42FABB2BBFA92C00 
     0xBBFA92C0 

Sądzę więc, że jednym z odlewów jest popychanie bity bezpośrednio do int. Tak więc -2147483648 jest większą tajemnicą.

+0

interesujące. Wydaje się, że istnieje różnica między sprawdzonymi i niezaznaczonymi zachowaniami rzutowania.Korzystając z zaawansowanej aplikacji "Kalkulator" (oczywiście w trybie "Programmer" ;-) Widzę, że '-1141206336' pasuje do obciętego (32 lsb)' 470259123000000'. Sprawdzona wersja wydaje się zwracać 'int.MinValue'. Jestem pewien, że ktoś będzie mógł wskazać to w specyfikacji językowej. – Alex

Odpowiedz

3

Nie jestem do końca pewny przyczyny, ale wygląda na błąd kompilatora, ponieważ program skompilowany z Roslyn daje tę samą wartość (-2147483648) dla obu wyrażeń.

Kompilator może oceniać wyrażenia stałe podczas kompilacji. Wszystkie konwersje z niesprawdzonym wyrażeniem są wykonywane przez kompilator, ale w drugim przypadku są wykonywane w czasie wykonywania przez CLR, więc zawsze istnieje możliwość, że używają nieco innych reguł. Jak zauważyłeś, kompilator wydaje się skracać inaczej niż środowisko wykonawcze, aby dopasować wartość do 32-bitowej liczby całkowitej. Możesz zobaczyć w podstawowej IL, że program właśnie ładuje stałą wartość (0xbbfa92c0) w miejsce niezaznaczonego wyrażenia.

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

dekompilowana IL od NET 4,5 kompilator:

.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