ก่อนอื่นคุณกำลังใช้บริบทที่ไม่ได้ตรวจสอบซึ่งเป็นคำสั่งสำหรับคอมไพเลอร์ที่คุณแน่ใจว่าเป็นนักพัฒนาว่าผลลัพธ์จะไม่ล้นประเภทและคุณต้องการเห็นข้อผิดพลาดในการรวบรวม ในสถานการณ์ของคุณจริง ๆ แล้วคุณมีวัตถุประสงค์ในการโอเวอร์โฟลว์ประเภทและคาดว่าจะมีพฤติกรรมที่สอดคล้องกันในคอมไพเลอร์สามตัวซึ่งหนึ่งในนั้นอาจจะเข้ากันได้ย้อนหลังไปไกลจนถึงประวัติศาสตร์เมื่อเปรียบเทียบกับ Roslyn และ. NET Core
สิ่งที่สองคือคุณกำลังผสมการแปลงโดยนัยและชัดเจน ฉันไม่แน่ใจเกี่ยวกับคอมไพเลอร์ Roslyn แต่คอมไพเลอร์. NET Framework และ. NET Core อาจใช้การเพิ่มประสิทธิภาพที่แตกต่างกันสำหรับการดำเนินการเหล่านั้น
ปัญหาที่นี่คือบรรทัดแรกของรหัสของคุณใช้เฉพาะค่า / ประเภทจุดลอยตัว แต่บรรทัดที่สองคือการรวมกันของค่า / ชนิดจุดลอยตัวและค่า / ชนิดสำคัญ
ในกรณีที่คุณทำจำนวนจุดลอยตัวจำนวนเต็มทันที (7> 7.0) คุณจะได้รับผลลัพธ์ที่เหมือนกันมากสำหรับแหล่งข้อมูลทั้งสามที่รวบรวม
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
}
}
ดังนั้นฉันจะพูดตรงข้ามกับสิ่งที่ V0ldek ตอบและนั่นคือ "ข้อผิดพลาด (ถ้าเป็นข้อบกพร่องจริงๆ) มีแนวโน้มมากที่สุดในคอมไพเลอร์ Roslyn และ. NET Core"
อีกเหตุผลหนึ่งที่เชื่อว่านั่นคือผลลัพธ์ของผลลัพธ์การคำนวณที่ไม่ได้ตรวจสอบครั้งแรกนั้นเหมือนกันสำหรับทุกคนและเป็นค่าที่เกินมูลค่าสูงสุดของUInt32
ประเภท
Console.WriteLine(unchecked((uint)(ulong)(1.2 * scale * scale + 1.5 * scale) - UInt32.MaxValue - 1)); // 859091763
ลบหนึ่งอยู่ตรงนั้นขณะที่เราเริ่มจากศูนย์ซึ่งเป็นค่าที่ยากต่อการลบตัวเอง หากความเข้าใจทางคณิตศาสตร์ของฉันเกี่ยวกับการโอเวอร์โฟลว์นั้นถูกต้องเราจะเริ่มจากจำนวนถัดไปหลังจากค่าสูงสุด
UPDATE
ตามความคิดเห็น jalsh
7.0 เป็น double ไม่ใช่ float ลอง 7.0f มันจะยังคงให้ 0
ความคิดเห็นของเขาถูกต้อง ในกรณีที่เราใช้ float คุณยังได้รับ 0 สำหรับ Roslyn และ. NET Core แต่ในทางกลับกันโดยใช้ผลลัพธ์สองเท่าใน 7
ฉันได้ทำการทดสอบเพิ่มเติมและสิ่งต่าง ๆ ก็ยิ่งเลวร้ายลง แต่ในที่สุดทุกอย่างก็สมเหตุสมผล (อย่างน้อยสักนิด)
สิ่งที่ฉันถือว่าเป็น. NET Framework 4.7.2 คอมไพเลอร์ (เปิดตัวในช่วงกลางปี 2018) ใช้การเพิ่มประสิทธิภาพที่แตกต่างจากคอมไพเลอร์. NET Core 3.1 และ Roslyn 3.4 (ออกเมื่อสิ้นปี 2019) การเพิ่มประสิทธิภาพ / การคำนวณที่แตกต่างกันเหล่านี้ใช้สำหรับค่าคงที่ที่ทราบ ณ เวลารวบรวม นั่นคือเหตุผลที่มีความจำเป็นต้องใช้unchecked
คำสำคัญเนื่องจากคอมไพเลอร์รู้อยู่แล้วว่ามีเหตุการณ์ล้นเกิดขึ้น แต่การคำนวณที่แตกต่างกันนั้นถูกใช้เพื่อปรับ IL ขั้นสุดท้ายให้เหมาะสม
ซอร์สโค้ดเดียวกันและ IL เดียวกันเกือบทั้งหมดยกเว้นคำสั่ง IL_000a หนึ่งคอมไพเลอร์คำนวณ 7 และ 0 อื่น ๆ
รหัสแหล่งที่มา
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
สาขาคอมไพเลอร์ของ Roslyn (ก.ย. 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
มันเริ่มต้นอย่างถูกต้องเมื่อคุณเพิ่มนิพจน์ที่ไม่คงที่ (โดยค่าเริ่มต้นunchecked
) ตามด้านล่าง
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
}
}
ซึ่งสร้าง IL ที่เหมือนกันทุกประการโดยคอมไพเลอร์ทั้งคู่
.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
สาขาคอมไพเลอร์ของ Roslyn (ก.ย. 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
ดังนั้นในที่สุดฉันเชื่อว่าสาเหตุของพฤติกรรมที่แตกต่างกันเป็นเพียงกรอบรุ่นที่แตกต่างกันและ / หรือคอมไพเลอร์ที่ใช้การเพิ่มประสิทธิภาพ / การคำนวณที่แตกต่างกันสำหรับการแสดงออกอย่างต่อเนื่อง แต่ในกรณีอื่น ๆ พฤติกรรมจะเหมือนกันมาก
ulong
กำลังถูกละเว้นในกรณีหลังดังนั้นจึงเกิดขึ้นในการแปลงfloat
->int