Trước hết, bạn đang sử dụng bối cảnh không được kiểm tra, đó là một hướng dẫn cho trình biên dịch mà bạn chắc chắn, với tư cách là nhà phát triển, kết quả sẽ không tràn loại và bạn muốn thấy không có lỗi biên dịch. Trong kịch bản của bạn, bạn thực sự có mục đích tràn loại và mong đợi hành vi nhất quán trên ba trình biên dịch khác nhau mà một trong số chúng có thể tương thích ngược với lịch sử so với Roslyn và .NET Core là các trình biên dịch mới.
Điều thứ hai là bạn đang trộn các chuyển đổi ngầm và rõ ràng. Tôi không chắc chắn về trình biên dịch Roslyn, nhưng chắc chắn trình biên dịch .NET Framework và .NET Core có thể sử dụng các tối ưu hóa khác nhau cho các hoạt động đó.
Vấn đề ở đây là dòng đầu tiên của mã của bạn chỉ sử dụng các giá trị / loại dấu phẩy động, nhưng dòng thứ hai là sự kết hợp của các giá trị / loại dấu phẩy động và giá trị / loại tích phân.
Trong trường hợp bạn thực hiện loại dấu phẩy động số nguyên ngay lập tức (7> 7.0), bạn sẽ nhận được kết quả rất giống nhau cho cả ba nguồn được biên dịch.
using System;
public class Program
{
const float scale = 64 * 1024;
public static void Main()
{
Console.WriteLine(unchecked((uint)(ulong)(1.2 * scale * scale + 1.5 * scale))); // 859091763
Console.WriteLine(unchecked((uint)(ulong)(scale * scale + 7.0))); // 7
}
}
Vì vậy, tôi sẽ nói ngược lại với những gì V0ldek đã trả lời và đó là "Lỗi (nếu đó thực sự là một lỗi) rất có thể trong trình biên dịch Roslyn và .NET Core".
Một lý do khác để tin rằng đó là kết quả của các kết quả tính toán không được kiểm tra đầu tiên là giống nhau cho tất cả và đó là giá trị vượt quá giá trị tối đa của UInt32
loại.
Console.WriteLine(unchecked((uint)(ulong)(1.2 * scale * scale + 1.5 * scale) - UInt32.MaxValue - 1)); // 859091763
Trừ đi một là có khi chúng ta bắt đầu từ số không, đó là giá trị khó có thể tự trừ. Nếu sự hiểu biết toán học của tôi về tràn là chính xác, chúng tôi bắt đầu từ số tiếp theo sau giá trị tối đa.
CẬP NHẬT
Theo bình luận của jalsh
7.0 là gấp đôi, không phải là số float, hãy thử 7.0f, nó vẫn sẽ cho bạn 0
Nhận xét của anh ấy là chính xác. Trong trường hợp chúng tôi sử dụng float, bạn vẫn nhận được 0 cho Roslyn và .NET Core, nhưng mặt khác sử dụng kết quả gấp đôi trong 7.
Tôi đã thực hiện một số thử nghiệm bổ sung và mọi thứ thậm chí còn kỳ lạ hơn, nhưng cuối cùng mọi thứ đều có ý nghĩa (ít nhất là một chút).
Những gì tôi giả định là trình biên dịch .NET Framework 4.7.2 (phát hành vào giữa năm 2018) thực sự sử dụng các tối ưu hóa khác nhau so với các trình biên dịch .NET Core 3.1 và Roslyn 3.4 (phát hành vào cuối năm 2019). Các tối ưu hóa / tính toán khác nhau này hoàn toàn được sử dụng cho các giá trị không đổi được biết tại thời gian biên dịch. Đó là lý do tại sao cần phải sử dụng unchecked
từ khóa vì trình biên dịch đã biết có sự cố tràn xảy ra, nhưng tính toán khác nhau đã được sử dụng để tối ưu hóa IL cuối cùng.
Mã nguồn giống nhau và gần như cùng IL ngoại trừ hướng dẫn IL_000a. Một trình biên dịch tính 7 và 0 khác.
Mã nguồn
using System;
public class Program
{
const float scale = 64 * 1024;
public static void Main()
{
Console.WriteLine(unchecked((uint)(ulong)(1.2 * scale * scale + 1.5 * scale)));
Console.WriteLine(unchecked((uint)(scale * scale + 7.0)));
}
}
.NET Framework (x64) IL
.class private auto ansi '<Module>'
{
} // end of class <Module>
.class public auto ansi beforefieldinit Program
extends [mscorlib]System.Object
{
// Fields
.field private static literal float32 scale = float32(65536)
// Methods
.method public hidebysig static
void Main () cil managed
{
// Method begins at RVA 0x2050
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldc.i4 859091763
IL_0005: call void [mscorlib]System.Console::WriteLine(uint32)
IL_000a: ldc.i4.7
IL_000b: call void [mscorlib]System.Console::WriteLine(uint32)
IL_0010: ret
} // end of method Program::Main
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2062
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class Program
Chi nhánh trình biên dịch Roslyn (tháng 9 năm 2019) IL
.class private auto ansi '<Module>'
{
} // end of class <Module>
.class public auto ansi beforefieldinit Program
extends [System.Private.CoreLib]System.Object
{
// Fields
.field private static literal float32 scale = float32(65536)
// Methods
.method public hidebysig static
void Main () cil managed
{
// Method begins at RVA 0x2050
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldc.i4 859091763
IL_0005: call void [System.Console]System.Console::WriteLine(uint32)
IL_000a: ldc.i4.0
IL_000b: call void [System.Console]System.Console::WriteLine(uint32)
IL_0010: ret
} // end of method Program::Main
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2062
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class Program
Nó bắt đầu đi đúng hướng khi bạn thêm các biểu thức không cố định (theo mặc định là unchecked
) như bên dưới.
using System;
public class Program
{
static Random random = new Random();
public static void Main()
{
var scale = 64 * random.Next(1024, 1025);
uint f = (uint)(ulong)(scale * scale + 7f);
uint d = (uint)(ulong)(scale * scale + 7d);
uint i = (uint)(ulong)(scale * scale + 7);
Console.WriteLine((uint)(ulong)(1.2 * scale * scale + 1.5 * scale)); // 859091763
Console.WriteLine((uint)(ulong)(scale * scale + 7f)); // 7
Console.WriteLine(f); // 7
Console.WriteLine((uint)(ulong)(scale * scale + 7d)); // 7
Console.WriteLine(d); // 7
Console.WriteLine((uint)(ulong)(scale * scale + 7)); // 7
Console.WriteLine(i); // 7
}
}
Mà tạo ra "chính xác" cùng IL bởi cả hai trình biên dịch.
.NET Framework (x64) IL
.class private auto ansi '<Module>'
{
} // end of class <Module>
.class public auto ansi beforefieldinit Program
extends [mscorlib]System.Object
{
// Fields
.field private static class [mscorlib]System.Random random
// Methods
.method public hidebysig static
void Main () cil managed
{
// Method begins at RVA 0x2050
// Code size 164 (0xa4)
.maxstack 4
.locals init (
[0] int32,
[1] uint32,
[2] uint32
)
IL_0000: ldc.i4.s 64
IL_0002: ldsfld class [mscorlib]System.Random Program::random
IL_0007: ldc.i4 1024
IL_000c: ldc.i4 1025
IL_0011: callvirt instance int32 [mscorlib]System.Random::Next(int32, int32)
IL_0016: mul
IL_0017: stloc.0
IL_0018: ldloc.0
IL_0019: ldloc.0
IL_001a: mul
IL_001b: conv.r4
IL_001c: ldc.r4 7
IL_0021: add
IL_0022: conv.u8
IL_0023: conv.u4
IL_0024: ldloc.0
IL_0025: ldloc.0
IL_0026: mul
IL_0027: conv.r8
IL_0028: ldc.r8 7
IL_0031: add
IL_0032: conv.u8
IL_0033: conv.u4
IL_0034: stloc.1
IL_0035: ldloc.0
IL_0036: ldloc.0
IL_0037: mul
IL_0038: ldc.i4.7
IL_0039: add
IL_003a: conv.i8
IL_003b: conv.u4
IL_003c: stloc.2
IL_003d: ldc.r8 1.2
IL_0046: ldloc.0
IL_0047: conv.r8
IL_0048: mul
IL_0049: ldloc.0
IL_004a: conv.r8
IL_004b: mul
IL_004c: ldc.r8 1.5
IL_0055: ldloc.0
IL_0056: conv.r8
IL_0057: mul
IL_0058: add
IL_0059: conv.u8
IL_005a: conv.u4
IL_005b: call void [mscorlib]System.Console::WriteLine(uint32)
IL_0060: ldloc.0
IL_0061: ldloc.0
IL_0062: mul
IL_0063: conv.r4
IL_0064: ldc.r4 7
IL_0069: add
IL_006a: conv.u8
IL_006b: conv.u4
IL_006c: call void [mscorlib]System.Console::WriteLine(uint32)
IL_0071: call void [mscorlib]System.Console::WriteLine(uint32)
IL_0076: ldloc.0
IL_0077: ldloc.0
IL_0078: mul
IL_0079: conv.r8
IL_007a: ldc.r8 7
IL_0083: add
IL_0084: conv.u8
IL_0085: conv.u4
IL_0086: call void [mscorlib]System.Console::WriteLine(uint32)
IL_008b: ldloc.1
IL_008c: call void [mscorlib]System.Console::WriteLine(uint32)
IL_0091: ldloc.0
IL_0092: ldloc.0
IL_0093: mul
IL_0094: ldc.i4.7
IL_0095: add
IL_0096: conv.i8
IL_0097: conv.u4
IL_0098: call void [mscorlib]System.Console::WriteLine(uint32)
IL_009d: ldloc.2
IL_009e: call void [mscorlib]System.Console::WriteLine(uint32)
IL_00a3: ret
} // end of method Program::Main
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2100
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
.method private hidebysig specialname rtspecialname static
void .cctor () cil managed
{
// Method begins at RVA 0x2108
// Code size 11 (0xb)
.maxstack 8
IL_0000: newobj instance void [mscorlib]System.Random::.ctor()
IL_0005: stsfld class [mscorlib]System.Random Program::random
IL_000a: ret
} // end of method Program::.cctor
} // end of class Program
Chi nhánh trình biên dịch Roslyn (tháng 9 năm 2019) IL
.class private auto ansi '<Module>'
{
} // end of class <Module>
.class public auto ansi beforefieldinit Program
extends [System.Private.CoreLib]System.Object
{
// Fields
.field private static class [System.Private.CoreLib]System.Random random
// Methods
.method public hidebysig static
void Main () cil managed
{
// Method begins at RVA 0x2050
// Code size 164 (0xa4)
.maxstack 4
.locals init (
[0] int32,
[1] uint32,
[2] uint32
)
IL_0000: ldc.i4.s 64
IL_0002: ldsfld class [System.Private.CoreLib]System.Random Program::random
IL_0007: ldc.i4 1024
IL_000c: ldc.i4 1025
IL_0011: callvirt instance int32 [System.Private.CoreLib]System.Random::Next(int32, int32)
IL_0016: mul
IL_0017: stloc.0
IL_0018: ldloc.0
IL_0019: ldloc.0
IL_001a: mul
IL_001b: conv.r4
IL_001c: ldc.r4 7
IL_0021: add
IL_0022: conv.u8
IL_0023: conv.u4
IL_0024: ldloc.0
IL_0025: ldloc.0
IL_0026: mul
IL_0027: conv.r8
IL_0028: ldc.r8 7
IL_0031: add
IL_0032: conv.u8
IL_0033: conv.u4
IL_0034: stloc.1
IL_0035: ldloc.0
IL_0036: ldloc.0
IL_0037: mul
IL_0038: ldc.i4.7
IL_0039: add
IL_003a: conv.i8
IL_003b: conv.u4
IL_003c: stloc.2
IL_003d: ldc.r8 1.2
IL_0046: ldloc.0
IL_0047: conv.r8
IL_0048: mul
IL_0049: ldloc.0
IL_004a: conv.r8
IL_004b: mul
IL_004c: ldc.r8 1.5
IL_0055: ldloc.0
IL_0056: conv.r8
IL_0057: mul
IL_0058: add
IL_0059: conv.u8
IL_005a: conv.u4
IL_005b: call void [System.Console]System.Console::WriteLine(uint32)
IL_0060: ldloc.0
IL_0061: ldloc.0
IL_0062: mul
IL_0063: conv.r4
IL_0064: ldc.r4 7
IL_0069: add
IL_006a: conv.u8
IL_006b: conv.u4
IL_006c: call void [System.Console]System.Console::WriteLine(uint32)
IL_0071: call void [System.Console]System.Console::WriteLine(uint32)
IL_0076: ldloc.0
IL_0077: ldloc.0
IL_0078: mul
IL_0079: conv.r8
IL_007a: ldc.r8 7
IL_0083: add
IL_0084: conv.u8
IL_0085: conv.u4
IL_0086: call void [System.Console]System.Console::WriteLine(uint32)
IL_008b: ldloc.1
IL_008c: call void [System.Console]System.Console::WriteLine(uint32)
IL_0091: ldloc.0
IL_0092: ldloc.0
IL_0093: mul
IL_0094: ldc.i4.7
IL_0095: add
IL_0096: conv.i8
IL_0097: conv.u4
IL_0098: call void [System.Console]System.Console::WriteLine(uint32)
IL_009d: ldloc.2
IL_009e: call void [System.Console]System.Console::WriteLine(uint32)
IL_00a3: ret
} // end of method Program::Main
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2100
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
.method private hidebysig specialname rtspecialname static
void .cctor () cil managed
{
// Method begins at RVA 0x2108
// Code size 11 (0xb)
.maxstack 8
IL_0000: newobj instance void [System.Private.CoreLib]System.Random::.ctor()
IL_0005: stsfld class [System.Private.CoreLib]System.Random Program::random
IL_000a: ret
} // end of method Program::.cctor
} // end of class Program
Vì vậy, cuối cùng tôi tin rằng lý do cho các hành vi khác nhau chỉ là một phiên bản khác nhau của khung và / hoặc trình biên dịch đang sử dụng các tối ưu hóa / tính toán khác nhau cho các biểu thức không đổi, nhưng trong các trường hợp khác thì hành vi rất giống nhau.
ulong
đang bị bỏ qua trong trường hợp sau, vì vậy nó xảy ra trongfloat
->int
chuyển đổi.