¿Hay alguna diferencia entre "double val = 1;" y "doble val = 1D;"

17

¿Hay alguna diferencia entre las siguientes dos piezas 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;
    }

}

Descubrí que nuestra base de código usa la segunda forma de escritura.

srnldai
fuente
Fuera de mi cabeza, el primero implica una promoción larga a doble.
Tanveer Badar
2
primero hace la conversión implícita a doble, segundo no hace ninguna conversión.
Serkan Arslan
2
El título de su pregunta y la pregunta en el cuerpo no coinciden, y las dos preguntas tienen respuestas diferentes.
Eric Lippert
1
@Eric Lippert De hecho, el cuerpo de la pregunta ha sido editado por otros usuarios.
srnldai
1
@Brian: No, el póster original es correcto; la edición cambió el significado de la pregunta, que no noté. La pregunta original era "¿Hay una diferencia ...? Además, ¿hay una diferencia ...?", Mi énfasis. El "Más" eliminado indica que el póster original se dio cuenta de que estaban haciendo dos preguntas que podrían tener respuestas diferentes. Esa es una mala práctica; idealmente, las preguntas deben hacer una sola pregunta. Pero la edición hace que parezca que las dos preguntas están destinadas a ser la misma pregunta, no dos preguntas diferentes.
Eric Lippert

Respuestas:

18

Aquí hay dos preguntas y es importante tener en cuenta que tienen respuestas diferentes.

¿Hay alguna diferencia entre double val = 1;y double val = 1D;?

No. El compilador de C # reconoce cuándo se usa un literal entero en un contexto en el que se espera un doble y cambia el tipo en el momento de la compilación, por lo que estos dos fragmentos generarán el mismo código.

¿Hay alguna diferencia entre las siguientes dos piezas de código?

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

Si. La regla de que las constantes enteras se cambian automáticamente a dobles solo se aplica a las constantes , y src ? ...no es una constante . El compilador generará el primero como si usted hubiera escrito:

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

Y el segundo como

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

Es decir, en el primero elegimos un número entero y luego lo convertimos a doble, y en el segundo elegimos un doble.

FYI: el compilador de C # o el jitter pueden reconocer que el primer programa se puede optimizar en el segundo, pero no sé si realmente lo hace. El compilador de C # no se mueven a veces conversiones para levantado aritmética en los cuerpos de los condicionales; Escribí ese código hace unos ocho años, pero no recuerdo todos los detalles.

Eric Lippert
fuente
6

No es una diferencia en el código IL generado.

Esta clase:

class Test1
{
    public readonly double Val;

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

Produce este código IL para el constructor:

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

Y esta clase:

class Test2
{
    public readonly double Val;

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

Produce este código IL para el constructor:

.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 puede ver, en la primera versión tiene que llamar conv.r8para convertir un int en un doble.

Sin embargo: (1) El resultado final es idéntico y (2) el compilador JIT puede traducir ambos al mismo código de máquina.

Así que la respuesta es: Sí, no es una diferencia - pero no uno que tiene que preocuparse.

Personalmente, optaría por la segunda versión, ya que expresa mejor la intención del programador y puede producir un código muy, muy ligeramente más eficiente (dependiendo de lo que haga el compilador JIT).

Matthew Watson
fuente
4

No hay diferencia, el compilador es lo suficientemente inteligente como para hacer una conversión implícitamente o no.
Sin embargo, si usa var, debe escribir var val = 42D;para asegurarse de que la variable sea doble y no 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
fuente