Có sự khác biệt nào giữa những lần tăng gấp đôi của val = 1; và đôi vali = 1D;


17

Có sự khác biệt giữa hai đoạn mã sau đây không?

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

}

Tôi thấy rằng cơ sở mã của chúng tôi sử dụng cách viết thứ hai.


Ngoài đỉnh đầu của tôi, đầu tiên liên quan đến một chương trình khuyến mãi dài đến gấp đôi.
— Tanveer Badar

2
đầu tiên thực hiện chuyển đổi ngầm thành gấp đôi, thứ hai không thực hiện bất kỳ chuyển đổi nào.
— Serkan Arslan

2
Tiêu đề câu hỏi của bạn và câu hỏi trong cơ thể không khớp nhau, và hai câu hỏi có câu trả lời khác nhau.
— Eric Lippert

1
@Eric Lippert Trên thực tế, phần chính của câu hỏi đã được chỉnh sửa bởi những người dùng khác.
— srnldai

1
@Brian: Không, poster gốc là chính xác; Bản chỉnh sửa đã thay đổi ý nghĩa của câu hỏi mà tôi không nhận thấy. Câu hỏi ban đầu là "Có sự khác biệt ...? Hơn nữa, có sự khác biệt ...?", Tôi nhấn mạnh. Việc xóa "Hơn nữa" chỉ ra rằng người đăng ban đầu nhận ra rằng họ đang hỏi hai câu hỏi có thể có câu trả lời khác nhau. Đó là một thực tế xấu; câu hỏi lý tưởng nên hỏi một câu hỏi duy nhất. Nhưng việc chỉnh sửa làm cho có vẻ như hai câu hỏi được dự định là cùng một câu hỏi, không phải là hai câu hỏi khác nhau.
— Eric Lippert

Câu trả lời:


18

Có hai câu hỏi ở đây và điều quan trọng cần lưu ý là chúng có câu trả lời khác nhau.

Có sự khác biệt giữa double val = 1;và double val = 1D;?

Không. Trình biên dịch C # nhận ra khi một số nguyên được sử dụng trong ngữ cảnh dự kiến ​​sẽ nhân đôi và loại thay đổi khi biên dịch, vì vậy hai đoạn này sẽ tạo ra cùng một mã.

Có sự khác biệt giữa hai đoạn mã sau đây không?

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

Đúng. Quy tắc các hằng số nguyên được tự động thay đổi thành gấp đôi chỉ áp dụng cho các hằng số và src ? ...không phải là hằng số . Trình biên dịch sẽ tạo ra cái trước như thể bạn đã viết:

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

Và thứ hai là

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

Nghĩa là, trong lần đầu tiên, chúng ta chọn một số nguyên và sau đó chuyển đổi nó thành gấp đôi và trong lần thứ hai, chúng ta chọn một số nguyên.

FYI: trình biên dịch C # hoặc jitter được phép nhận ra rằng chương trình đầu tiên có thể được tối ưu hóa thành chương trình thứ hai, nhưng tôi không biết liệu nó có thực sự làm như vậy không. Các biên dịch C # không đôi khi di chuyển chuyển đổi cho nâng số học vào các cơ quan của điều kiện; Tôi đã viết mã đó khoảng tám năm trước, nhưng tôi không nhớ tất cả các chi tiết.


6

Có là một sự khác biệt trong các mã IL được tạo ra.

Lớp này:

class Test1
{
    public readonly double Val;

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

Tạo mã IL này cho hàm tạo:

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

Và lớp học này:

class Test2
{
    public readonly double Val;

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

Tạo mã IL này cho hàm tạo:

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

Như bạn có thể thấy, trong phiên bản đầu tiên, nó phải gọi conv.r8để chuyển đổi một int thành gấp đôi.

Tuy nhiên: (1) Kết quả cuối cùng giống hệt nhau và (2) trình biên dịch JIT có thể dịch cả hai thứ này sang cùng một mã máy.

Vì vậy, câu trả lời là: Có, có là một sự khác biệt - nhưng không phải là một mà bạn cần phải lo lắng về.

Cá nhân, tôi sẽ dùng phiên bản thứ hai vì nó thể hiện rõ hơn ý định của lập trình viên và có thể tạo ra mã hiệu quả hơn một chút (tùy thuộc vào trình biên dịch JIT làm gì).


4

Không có sự khác biệt, trình biên dịch có đủ thông minh để thực hiện chuyển đổi hay không.
Tuy nhiên, nếu bạn sử dụng var, bạn cần phải viết var val = 42D;để đảm bảo biến là gấp đôi chứ không phải 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 !! /!\
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.