Y a-t-il une différence entre "double val = 1;" et "double val = 1D;"?

17

Y a-t-il une différence entre les deux morceaux de code suivants?

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

}

J'ai trouvé que notre base de code utilise la deuxième façon d'écrire.

srnldai
la source
Du haut de ma tête, la première implique une promotion longue à double.
Tanveer Badar
2
la première effectue une conversion implicite en double, la seconde n'effectue aucune conversion.
Serkan Arslan
2
Le titre de votre question et la question dans le corps ne correspondent pas, et les deux questions ont des réponses différentes.
Eric Lippert
1
@Eric Lippert En fait, le corps de la question a été édité par d'autres utilisateurs.
srnldai
1
@Brian: Non, l'affiche originale est correcte; la modification a changé le sens de la question, ce que je n'ai pas remarqué. La question initiale était «Y a-t-il une différence ...? De plus, y a-t-il une différence ...? Le "Plus loin" supprimé indique que l'affiche originale s'est rendu compte qu'ils posaient deux questions qui pourraient avoir des réponses différentes. C'est une mauvaise pratique; les questions devraient idéalement poser une seule question. Mais le montage donne l'impression que les deux questions sont censées être la même question, pas deux questions différentes.
Eric Lippert

Réponses:

18

Il y a deux questions ici et il est important de noter qu'elles ont des réponses différentes.

Y a-t-il une différence entre double val = 1;et double val = 1D;?

Non. Le compilateur C # reconnaît quand un littéral entier est utilisé dans un contexte où un double est attendu et modifie le type au moment de la compilation, donc ces deux fragments généreront le même code.

Y a-t-il une différence entre les deux morceaux de code suivants?

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

Oui. La règle selon laquelle les constantes entières sont automatiquement remplacées par des doubles ne s'applique qu'aux constantes et src ? ...n'est pas une constante . Le compilateur générera le premier comme si vous aviez écrit:

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

Et le second comme

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

Autrement dit, dans le premier, nous choisissons un entier, puis le convertissons en double, et dans le second, nous choisissons un double.

Pour info: le compilateur C # ou la gigue sont autorisés à reconnaître que le premier programme peut être optimisé dans le second, mais je ne sais pas s'il le fait réellement. Le compilateur C # ne déplace parfois des conversions pour soulever l' arithmétique dans les corps de conditionals; J'ai écrit ce code il y a environ huit ans maintenant, mais je ne me souviens pas de tous les détails.

Eric Lippert
la source
6

Il existe une différence dans le code IL généré.

Cette classe:

class Test1
{
    public readonly double Val;

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

Produit ce code IL pour le constructeur:

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

Et cette classe:

class Test2
{
    public readonly double Val;

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

Produit ce code IL pour le constructeur:

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

Comme vous pouvez le voir, dans la première version, il doit appeler conv.r8pour convertir un int en double.

Cependant: (1) Le résultat final est identique et (2) le compilateur JIT peut bien traduire les deux dans le même code machine.

La réponse est donc: oui, il y a une différence - mais pas celle dont vous devez vous inquiéter.

Personnellement, j'opterais pour la deuxième version car cela exprime mieux l'intention du programmeur et peut produire un code très très légèrement plus efficace (en fonction de ce que le compilateur JIT obtient).

Matthew Watson
la source
4

Il n'y a aucune différence, le compilateur est suffisamment intelligent pour effectuer implicitement une conversion ou non.
Cependant, si vous utilisez var, vous devez écrire var val = 42D;pour vous assurer que la variable est un double et non un 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 !! /!\
Cid
la source