C'è una differenza tra “double val = 1;” e "double val = 1D;"?


17

C'è una differenza tra i seguenti due pezzi di codice?

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

}

Ho scoperto che la nostra base di codice utilizza il secondo modo di scrivere.


In cima alla mia testa, il primo prevede una promozione da lunga a doppia.
Tanveer Badar,

2
in primo luogo effettua la conversione implicita in doppio, in secondo luogo non esegue alcuna conversione.
Serkan Arslan,

2
Il titolo della domanda e la domanda nel corpo non corrispondono e le due domande hanno risposte diverse.
Eric Lippert,

1
@Eric Lippert In effetti, il corpo della domanda è stato modificato da altri utenti.
srnldai,

1
@Brian: No, il poster originale è corretto; la modifica ha cambiato il significato della domanda, che non ho notato. La domanda originale era "C'è una differenza ...? Inoltre, c'è una differenza ...?", La mia enfasi. Il "ulteriormente" rimosso indica che il poster originale si è reso conto che stavano ponendo due domande che potrebbero avere risposte diverse. Questa è una cattiva pratica; le domande dovrebbero idealmente porre una sola domanda. Ma la modifica fa sembrare che le due domande debbano essere la stessa domanda, non due domande diverse.
Eric Lippert,

Risposte:


18

Ci sono due domande qui ed è importante notare che hanno risposte diverse.

C'è una differenza tra double val = 1;e double val = 1D;?

No. Il compilatore C # riconosce quando viene utilizzato un valore letterale intero in un contesto in cui è previsto un doppio e il tipo cambia in fase di compilazione, quindi questi due frammenti genereranno lo stesso codice.

C'è una differenza tra i seguenti due pezzi di codice?

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

Sì. La regola secondo cui le costanti intere vengono automaticamente modificate in doppie si applica solo alle costanti e src ? ...non è una costante . Il compilatore genererà il primo come se avessi scritto:

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

E il secondo come

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

Cioè, nel primo scegliamo un numero intero e poi lo convertiamo in doppio, e nel secondo scegliamo un doppio.

Cordiali saluti: il compilatore C # o il jitter sono autorizzati a riconoscere che il primo programma può essere ottimizzato nel secondo, ma non so se lo fa effettivamente. Il compilatore C # non a volte muove conversioni per sollevare l'aritmetica nei corpi dei condizionali; Ho scritto quel codice circa otto anni fa, ma non ricordo tutti i dettagli.


6

V'è una differenza nel codice IL generato.

Questa classe:

class Test1
{
    public readonly double Val;

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

Produce questo codice IL per il costruttore:

.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
    }
}

E questa classe:

class Test2
{
    public readonly double Val;

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

Produce questo codice IL per il costruttore:

.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
    }
}

Come puoi vedere, nella prima versione deve chiamare conv.r8per convertire un int in doppio.

Tuttavia: (1) Il risultato finale è identico e (2) il compilatore JIT potrebbe tradurre entrambi questi nello stesso codice macchina.

Quindi la risposta è: sì, c'è una differenza - ma non uno che avete bisogno di preoccuparsi.

Personalmente, sceglierei la seconda versione poiché esprime meglio l'intento del programmatore e potrebbe produrre un codice molto molto più efficiente (a seconda di cosa si occupa il compilatore JIT).


4

Non c'è alcuna differenza, il compilatore è abbastanza intelligente da fare implicitamente una conversione o no.
Tuttavia, se lo usi var, devi scrivere var val = 42D;per assicurarti che la variabile sia doppia e non int.

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 !! /!\
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.