Чи є різниця між "подвійним вал = 1;" і "подвійний val = 1D;"


17

Чи є різниця між наступними двома фрагментами коду?

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

}

Я виявив, що наша база коду використовує другий спосіб написання.


У верхній частині моєї голови, перший передбачає тривале подвійне просування.
Tanveer Badar

2
перший робить неявне перетворення у подвійне, друге не робить ніякого перетворення.
Серкан Арслан

2
Назва вашого запитання та питання в тілі не збігаються, а на два запитання є різні відповіді.
Ерік Ліпперт

1
@Eric Lippert Насправді основну частину питання редагували інші користувачі.
srnldai

1
@Brian: Ні, оригінальний плакат правильний; редакція змінила значення питання, яке я не помітив. Первісне питання було "Чи є різниця ...? Далі, чи є різниця ...?", Моє наголос. Вилучене "Далі" вказує на те, що оригінальний плакат зрозумів, що вони задають два питання, які можуть мати різні відповіді. Це погана практика; питання в ідеалі повинні ставити одне запитання. Але редагування здається, що два питання призначені для одного і того ж питання, а не двох різних.
Ерік Ліпперт

Відповіді:


18

Тут є два питання, і важливо зазначити, що вони мають різні відповіді.

Чи є різниця між double val = 1;і double val = 1D;?

Ні. Компілятор C # розпізнає, коли використовується цілий літерал у контексті, коли очікується подвійне і чи змінюється тип під час компіляції, тож ці два фрагменти будуть генерувати той самий код.

Чи є різниця між наступними двома фрагментами коду?

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

Так. Правило про те, що цілі константи автоматично змінюються на подвійні, застосовується лише до констант , і src ? ...не є постійною . Компілятор генерує колишній так, ніби ви написали:

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

А другий як

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

Тобто в першому ми вибираємо ціле число, а потім перетворюємо його в подвійне, а в другому вибираємо подвійне.

FYI: компілятор C # або тремтіння дозволяють визнати, що перша програма може бути оптимізована під другу, але я не знаю, чи це насправді так. C # компілятор робить іноді рухатися перетвореннями для піднятого арифметики в органи умовних; Я написав цей код десь вісім років тому, але не пам'ятаю всіх подробиць.


6

Там є різниця в створеному коді IL.

Цей клас:

class Test1
{
    public readonly double Val;

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

Виробляє цей IL код для конструктора:

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

І цей клас:

class Test2
{
    public readonly double Val;

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

Виробляє цей IL код для конструктора:

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

Як бачите, у першій версії він повинен викликати conv.r8перетворення int в подвійний.

Однак: (1) Кінцевий результат ідентичний, і (2) компілятор JIT цілком може перекласти обидва в той же машинний код.

Тож відповідь така: Так, є різниця - але не одна, про яку потрібно турбуватися.

Особисто я б пішов на другу версію, оскільки це краще виражає наміри програміста і може створити дуже дещо ефективніший код (залежно від того, що збирається компілятор JIT).


4

Немає різниці, компілятор досить розумний, щоб неявно зробити перетворення чи ні.
Однак якщо ви використовуєте var, вам потрібно написати, var val = 42D;щоб переконатися, що змінна є подвійною, а не цілою.

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 !! /!\
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.