Gibt es einen Unterschied zwischen "double val = 1"? und "double val = 1D;"?

17

Gibt es einen Unterschied zwischen den folgenden beiden Codeteilen?

class Test {

    public readonly double Val;

    public Test(bool src) {
        this.Val = src ? 1 : 0;
    }

}

class Test {

    public readonly double Val;

    public Test(bool src) {
        this.Val = src ? 1D : 0D;
    }

}

Ich fand heraus, dass unsere Codebasis die zweite Schreibweise verwendet.

srnldai
quelle
Die erste beinhaltet eine lange bis doppelte Beförderung.
Tanveer Badar
2
Erstens wird implizit in Double konvertiert, zweitens wird keine Konvertierung durchgeführt.
Serkan Arslan
2
Ihr Fragentitel und die Frage im Textkörper stimmen nicht überein, und die beiden Fragen haben unterschiedliche Antworten.
Eric Lippert
1
@ Eric Lippert Tatsächlich wurde der Text der Frage von anderen Benutzern bearbeitet.
Srnldai
1
@ Brian: Nein, das Originalposter ist korrekt. Die Bearbeitung änderte die Bedeutung der Frage, die ich nicht bemerkte. Die ursprüngliche Frage war : „Gibt es einen Unterschied ...? Außerdem gibt es einen Unterschied ...?“, Mein Schwerpunkt. Das entfernte "Weiter" zeigt an, dass das Originalplakat erkannt hat, dass zwei Fragen gestellt wurden, die möglicherweise unterschiedliche Antworten haben. Das ist eine schlechte Praxis; Fragen sollten idealerweise eine einzelne Frage stellen. Aber die Bearbeitung lässt es so aussehen, als ob die beiden Fragen dieselbe Frage sein sollen, nicht zwei verschiedene Fragen.
Eric Lippert

Antworten:

18

Hier gibt es zwei Fragen, und es ist wichtig zu beachten, dass sie unterschiedliche Antworten haben.

Gibt es einen Unterschied zwischen double val = 1;und double val = 1D;?

Nein. Der C # -Compiler erkennt, wenn ein Ganzzahlliteral in einem Kontext verwendet wird, in dem ein Double erwartet wird, und ändert den Typ zur Kompilierungszeit, sodass diese beiden Fragmente denselben Code generieren.

Gibt es einen Unterschied zwischen den folgenden beiden Codeteilen?

double Val; 
...    
this.Val = src ? 1 : 0;
---
this.Val = src ? 1D : 0D;

Ja. Die Regel, dass ganzzahlige Konstanten automatisch in doppelte Konstanten geändert werden, gilt nur für Konstanten und src ? ...ist keine Konstante . Der Compiler generiert den ersteren so, als hätten Sie geschrieben:

int t;
if (src)
  t = 1;
else
  t = 0;
this.Val = (double)t;

Und der zweite als

double t;
if (src)
  t = 1D;
else
  t = 0D;
this.Val = t;

Das heißt, im ersten wählen wir eine Ganzzahl und konvertieren sie dann in double, und im zweiten wählen wir ein double.

Zu Ihrer Information: Der C # -Compiler oder der Jitter dürfen erkennen, dass das erste Programm in das zweite optimiert werden kann, aber ich weiß nicht, ob dies tatsächlich der Fall ist. Der C # Compiler hat manchmal Umwandlungen bewegen für hob in die Körper von conditionals Arithmetik; Ich habe diesen Code vor ungefähr acht Jahren geschrieben, aber ich erinnere mich nicht an alle Details.

Eric Lippert
quelle
6

Dort gibt einen Unterschied im generierten IL-Code.

Diese Klasse:

class Test1
{
    public readonly double Val;

    public Test1(bool src)
    {
        this.Val = src ? 1 : 0;
    }
}

Erzeugt diesen IL-Code für den Konstruktor:

.class private auto ansi beforefieldinit Demo.Test1
    extends [mscorlib]System.Object
{
    .field public initonly float64 Val

    .method public hidebysig specialname rtspecialname instance void .ctor (
            bool src
        ) cil managed 
    {
        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        IL_0006: ldarg.0
        IL_0007: ldarg.1
        IL_0008: brtrue.s IL_000d

        IL_000a: ldc.i4.0
        IL_000b: br.s IL_000e

        IL_000d: ldc.i4.1

        IL_000e: conv.r8
        IL_000f: stfld float64 Demo.Test1::Val
        IL_0014: ret
    }
}

Und diese Klasse:

class Test2
{
    public readonly double Val;

    public Test2(bool src)
    {
        this.Val = src ? 1d : 0d;
    }
}

Erzeugt diesen IL-Code für den Konstruktor:

.class private auto ansi beforefieldinit Demo.Test2
    extends [mscorlib]System.Object
{
    .field public initonly float64 Val

    .method public hidebysig specialname rtspecialname instance void .ctor (
            bool src
        ) cil managed 
    {
        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        IL_0006: ldarg.0
        IL_0007: ldarg.1
        IL_0008: brtrue.s IL_0015

        IL_000a: ldc.r8 0.0
        IL_0013: br.s IL_001e

        IL_0015: ldc.r8 1

        IL_001e: stfld float64 Demo.Test2::Val
        IL_0023: ret
    }
}

Wie Sie sehen können, muss es in der ersten Version aufgerufen werden conv.r8 , um ein int in ein double zu konvertieren.

Jedoch: (1) Das Endergebnis ist identisch und (2) der JIT-Compiler kann beide in denselben Maschinencode übersetzen.

Die Antwort lautet also: Ja, das gibt es einen Unterschied - aber keinen, über den Sie sich Sorgen machen müssen.

Persönlich würde ich mich für die zweite Version entscheiden, da dies die Absicht des Programmierers besser zum Ausdruck bringt und möglicherweise einen sehr etwas effizienteren Code erzeugt (abhängig davon, was der JIT-Compiler vorhat).

Matthew Watson
quelle
4

Es gibt keinen Unterschied, der Compiler ist intelligent genug, um implizit eine Konvertierung durchzuführen oder nicht.
Wenn Sie jedoch verwenden var, müssen Sie schreiben var val = 42D;, um sicherzustellen, dass die Variable ein Double und kein Int ist.

double foo = 1;  // This is a double having the value 1
double bar = 1d; // This is a double having the value 1

var val = 42d;   // This is a double having the value 42
var val2 = 42;   // /!\ This is an int having the value 42 !! /!\
Cid
quelle