2015-03-25 17 views
8

Questo è davvero strano. Mi è stato rintracciare questo errore:Differente comportamento di fusione

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

... e si è scoperto che era a causa di codice come questo:

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

In effetti, quando si esegue questo LINQPad:

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

... mi dà:

-2147483648

Tuttavia, un altro sviluppatore qui dice che ottiene qualcosa di completamente diverso (non in LINQPad):

-1141206336

Quando cerco di valutare solo il cast da solo su una costante:

(int)470259123000000.0 

... I ottenere un errore di compilazione a causa del bisogno di unchecked. E questo:

unchecked((int)470259123000000.0) 

... viene valutata al -1141206336 come l'altro sviluppatore ha. Quindi ho pensato che forse lo Convert avesse creato un valore leggermente diverso dalla costante. No, questo restituisce True:

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

Cosa diavolo sta succedendo qui? Perché la valutazione di queste espressioni apparentemente identiche produce risultati così diversi?

Aggiornamento:

Trovato un suggerimento. La rappresentazione esadecimale di 4.70259123E14 e -1141206336 è:

0x42FABB2BBFA92C00 
     0xBBFA92C0 

quindi credo che uno dei calchi è spingendo i bit direttamente nel int. Quindi -2147483648 è il mistero più grande.

+0

Interessante. Sembra una differenza tra comportamento di casting controllato e non controllato.Usando l'applicazione "Calcolatrice" ad alta tecnologia (in modalità "Programmatore" ovviamente ;-) Vedo che '-1141206336' corrisponde al troncato (32 lsb) di' 470259123000000'. La versione verificata sembra restituire 'int.MinValue'. Sono sicuro che qualcuno sarà in grado di farlo notare nelle specifiche del linguaggio. – Alex

risposta

3

Non sono esattamente sicuro del motivo sottostante, ma sembra un errore del compilatore, perché un programma compilato con Roslyn fornisce lo stesso valore (-2147483648) per entrambe le espressioni.

Il compilatore può valutare le espressioni costanti in fase di compilazione. Tutte le conversioni con l'espressione deselezionata vengono eseguite dal compilatore, ma nell'altro caso vengono eseguite in fase di esecuzione dal CLR, pertanto esiste sempre la possibilità che utilizzino regole leggermente diverse. Come è stato osservato, il compilatore sembra troncare in modo diverso rispetto al runtime per adattare il valore a un numero intero a 32 bit. È possibile vedere nell'IL sottostante che il programma sta solo caricando il valore costante (0xbbfa92c0) al posto dell'espressione deselezionata.

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

decompilato IL da .NET 4.5 compilatore:

.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 
Problemi correlati