Existe uma diferença entre "double val = 1;" e "duplo val = 1D;"?

17

Existe uma diferença entre os dois seguintes pedaços de código?

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

}

Eu descobri que nossa base de código usa a segunda maneira de escrever.

srnldai
fonte
Em primeiro lugar, o primeiro envolve uma promoção longa a dupla.
Tanveer Badar
2
primeiro faz a conversão implícita para dobrar, o segundo não faz nenhuma conversão.
precisa
2
O título da sua pergunta e a pergunta no corpo não coincidem, e as duas perguntas têm respostas diferentes.
precisa
11
@ Eric Lippert De fato, o corpo da pergunta foi editado por outros usuários.
srnldai
11
@ Brian: Não, o pôster original está correto; a edição mudou o significado da pergunta, que eu não percebi. A pergunta original era "Existe uma diferença ...? Além disso, existe uma diferença ...?", Minha ênfase. O "Mais" removido indica que o pôster original percebeu que estavam fazendo duas perguntas que poderiam ter respostas diferentes. Essa é uma má prática; idealmente, as perguntas devem fazer uma única pergunta. Mas a edição faz parecer que as duas perguntas devem ser a mesma, não duas perguntas diferentes.
precisa

Respostas:

18

Há duas perguntas aqui e é importante observar que elas têm respostas diferentes.

Existe uma diferença entre double val = 1;e double val = 1D;?

Não. O compilador C # reconhece quando um literal inteiro é usado em um contexto em que um duplo é esperado e o tipo é alterado no tempo de compilação, portanto esses dois fragmentos geram o mesmo código.

Existe uma diferença entre os dois seguintes pedaços de código?

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

Sim. A regra de que constantes inteiras são alteradas automaticamente para dobras se aplica apenas a constantes e src ? ...não é uma constante . O compilador irá gerar o primeiro como se você tivesse escrito:

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

E o segundo como

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

Ou seja, no primeiro escolhemos um número inteiro e depois o convertemos para o dobro, e no segundo escolhemos um duplo.

FYI: o compilador C # ou o jitter têm permissão para reconhecer que o primeiro programa pode ser otimizado para o segundo, mas não sei se ele realmente o faz. Às vezes , o compilador C # move conversões para aritmética elevada nos corpos de condicionais; Escrevi esse código há oito anos, mas não me lembro de todos os detalhes.

Eric Lippert
fonte
6

Não é uma diferença no código IL gerado.

Esta aula:

class Test1
{
    public readonly double Val;

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

Produz este código IL para o construtor:

.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 esta classe:

class Test2
{
    public readonly double Val;

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

Produz este código IL para o construtor:

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

Como você pode ver, na primeira versão, é necessário chamar conv.r8para converter um int em um duplo.

No entanto: (1) o resultado final é idêntico e (2) o compilador JIT pode muito bem converter esses dois no mesmo código de máquina.

Portanto, a resposta é: Sim, há é uma diferença - mas não aquele que você precisa para se preocupar.

Pessoalmente, eu optaria pela segunda versão, pois ela expressa melhor a intenção do programador e pode produzir código muito ligeiramente mais eficiente (dependendo do que o compilador JIT aprende).

Matthew Watson
fonte
4

Não há diferença, o compilador é inteligente o suficiente para fazer uma conversão implicitamente ou não.
No entanto, se você usar var, precisará escrever var val = 42D;para garantir que a variável seja dupla e não 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
fonte