Apakah ada perbedaan antara “double val = 1;” dan “double val = 1D;”?

17

Apakah ada perbedaan antara dua bagian kode berikut?

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

}

Saya menemukan bahwa basis kode kami menggunakan cara penulisan yang kedua.

srnldai
sumber
Dari atas kepala saya, yang pertama melibatkan promosi panjang ke ganda.
Tanveer Badar
2
pertama membuat konversi implisit menjadi dua kali lipat, kedua tidak membuat konversi apa pun.
Serkan Arslan
2
Judul pertanyaan Anda dan pertanyaan dalam tubuh tidak cocok, dan dua pertanyaan memiliki jawaban yang berbeda.
Eric Lippert
1
@Eric Lippert Faktanya, isi pertanyaan telah diedit oleh pengguna lain.
srnldai
1
@Brian: Tidak, poster aslinya benar; hasil edit mengubah makna pertanyaan, yang tidak saya perhatikan. Pertanyaan aslinya adalah "Apakah ada perbedaan ...? Selanjutnya, apakah ada perbedaan ...?", Penekanan saya. "Lebih lanjut" yang dihapus menunjukkan bahwa poster asli menyadari bahwa mereka mengajukan dua pertanyaan yang mungkin memiliki jawaban yang berbeda. Itu praktik yang buruk; pertanyaan idealnya harus mengajukan satu pertanyaan. Tetapi hasil edit membuatnya tampak seperti dua pertanyaan yang dimaksudkan untuk menjadi pertanyaan yang sama, bukan dua pertanyaan yang berbeda.
Eric Lippert

Jawaban:

18

Ada dua pertanyaan di sini dan penting untuk dicatat bahwa mereka memiliki jawaban yang berbeda.

Apakah ada perbedaan antara double val = 1;dan double val = 1D;?

Tidak. Kompiler C # mengenali kapan integer literal digunakan dalam konteks di mana double diharapkan dan apakah tipe berubah pada waktu kompilasi, sehingga dua fragmen ini akan menghasilkan kode yang sama.

Apakah ada perbedaan antara dua bagian kode berikut?

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

Iya. Aturan bahwa konstanta integer secara otomatis diubah menjadi ganda hanya berlaku untuk konstanta , dan src ? ...bukan konstanta . Kompiler akan menghasilkan yang pertama seolah-olah Anda menulis:

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

Dan yang kedua sebagai

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

Yaitu, di pertama kita memilih integer dan kemudian mengubahnya menjadi dua kali lipat, dan di kedua kita memilih ganda.

FYI: kompiler C # atau jitter diizinkan untuk mengenali bahwa program pertama dapat dioptimalkan menjadi yang kedua, tapi saya tidak tahu apakah itu benar-benar melakukannya. Kompiler C # kadang - kadang memindahkan konversi untuk aritmatika terangkat ke tubuh kondisional; Saya menulis kode itu sekitar delapan tahun yang lalu sekarang, tetapi saya tidak ingat semua detailnya.

Eric Lippert
sumber
6

Ada adalah perbedaan dalam kode IL yang dihasilkan.

Kelas ini:

class Test1
{
    public readonly double Val;

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

Menghasilkan kode IL ini untuk 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
    }
}

Dan kelas ini:

class Test2
{
    public readonly double Val;

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

Menghasilkan kode IL ini untuk 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
    }
}

Seperti yang Anda lihat, dalam versi pertama ia harus memanggil conv.r8untuk mengkonversi int menjadi ganda.

Namun: (1) Hasil akhirnya identik dan (2) kompiler JIT dapat menerjemahkan keduanya ke kode mesin yang sama.

Jadi jawabannya adalah: Ya, ada adalah perbedaan - tapi tidak satu yang Anda perlu khawatir tentang.

Secara pribadi, saya akan menggunakan versi kedua karena itu lebih baik mengekspresikan maksud programmer, dan dapat menghasilkan kode yang sangat sangat sedikit lebih efisien (tergantung pada apa yang dilakukan kompiler JIT).

Matthew Watson
sumber
4

Tidak ada perbedaan, kompiler cukup pintar untuk secara implisit melakukan konversi atau tidak.
Namun, jika Anda gunakanvar , Anda perlu menulis var val = 42D;untuk memastikan variabelnya ganda dan bukan 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
sumber