Hành vi tràn C # cho uint không được kiểm tra


10

Tôi đã thử nghiệm mã này tại https://dotnetfiddle.net/ :

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)(ulong)(scale* scale + 7)));
    }
}

Nếu tôi biên dịch với .NET 4.7.2, tôi nhận được

859091763

7

Nhưng nếu tôi làm Roslyn hoặc .NET Core, tôi sẽ nhận được

859091763

0

Lý do tại sao điều này xảy ra?


Các diễn viên ulongđang bị bỏ qua trong trường hợp sau, vì vậy nó xảy ra trong float-> intchuyển đổi.
madreflection

Tôi ngạc nhiên hơn bởi sự thay đổi hành vi, đó dường như là một sự khác biệt khá lớn. Tôi sẽ không mong đợi "0" là một câu trả lời hợp lệ với chuỗi phôi đó.
Lukas

Có thể hiểu được. Một số thứ trong thông số kỹ thuật đã được sửa trong trình biên dịch khi họ xây dựng Roslyn, vì vậy đó có thể là một phần của nó. Kiểm tra đầu ra JIT trên phiên bản này trên SharpLab. Điều đó cho thấy các diễn viên ulongảnh hưởng đến kết quả như thế nào .
madreflection

Thật hấp dẫn, với ví dụ của bạn trở lại trên dotnetfiddle, WriteLine cuối cùng xuất ra 0 trong Roslyn 3.4 và 7 trên .NET Core 3.1
Lukas

Tôi cũng đã xác nhận trên máy tính để bàn của tôi. Mã JIT thậm chí không nhìn gần hết, tôi nhận được các kết quả khác nhau giữa .NET Core và .NET Framework. Trippy
Lukas

Câu trả lời:


1

Kết luận của tôi không chính xác. Xem bản cập nhật để biết thêm chi tiết.

Trông giống như một lỗi trong trình biên dịch đầu tiên bạn sử dụng. Không là kết quả chính xác trong trường hợp này . Thứ tự của các hoạt động được quy định bởi đặc tả C # như sau:

  1. nhân scalevới scale, năng suấta
  2. thực hiện a + 7, năng suấtb
  3. đúc bđể ulong, năng suấtc
  4. đúc cđể uint, năng suấtd

Hai thao tác đầu tiên để lại cho bạn một giá trị nổi là b = 4.2949673E+09f. Theo số học dấu phẩy động tiêu chuẩn, đây là 4294967296( bạn có thể kiểm tra nó ở đây ). Điều đó phù hợp với ulongtốt, vì vậy c = 4294967296, nhưng nó chính xác hơn một uint.MaxValue, vì vậy nó đi vòng quanh 0, do đó d = 0. Bây giờ, sự ngạc nhiên bất ngờ, kể từ dấu chấm động số học là funky, 4.2949673E+09f4.2949673E+09f + 7là con số chính xác cùng trong IEEE 754. Vì vậy, scale * scalesẽ cung cấp cho bạn cùng giá trị của một floatnhư scale * scale + 7, a = b, vì vậy các hoạt động thứ hai là cơ bản là một không-op.

Trình biên dịch Roslyn thực hiện (một số) các hoạt động const tại thời gian biên dịch và tối ưu hóa toàn bộ biểu thức này thành 0. Một lần nữa, đó là kết quả chính xác trình biên dịch được phép thực hiện bất kỳ tối ưu hóa nào sẽ dẫn đến hành vi chính xác giống như mã mà không có chúng.

Tôi đoán là trình biên dịch .NET 4.7.2 mà bạn đã sử dụng cũng cố gắng tối ưu hóa điều này, nhưng có một lỗi khiến nó đánh giá dàn diễn viên không đúng chỗ. Đương nhiên, nếu bạn lần đầu tiên scalechuyển sang một uintvà sau đó thực hiện thao tác, bạn sẽ nhận được 7, bởi vì scale * scalecác chuyến đi khứ hồi đến 0và sau đó bạn thêm 7. Nhưng điều đó không phù hợp với kết quả bạn sẽ nhận được khi đánh giá các biểu thức từng bước trong thời gian chạy . Một lần nữa, nguyên nhân gốc rễ chỉ là phỏng đoán khi xem xét hành vi được tạo ra, nhưng đưa ra mọi thứ tôi đã nêu ở trên tôi tin chắc đây là một vi phạm đặc tả ở phía trình biên dịch đầu tiên.

CẬP NHẬT:

Tôi đã làm một việc ngu ngốc. Có một chút đặc tả C # mà tôi không biết đã tồn tại khi viết câu trả lời trên:

Các phép toán dấu phẩy động có thể được thực hiện với độ chính xác cao hơn loại kết quả của phép toán. Ví dụ, một số kiến ​​trúc phần cứng hỗ trợ loại dấu phẩy động "mở rộng" hoặc "dài gấp đôi" với phạm vi và độ chính xác cao hơn loại kép và thực hiện ngầm tất cả các hoạt động của dấu phẩy động bằng loại chính xác cao hơn này. Chỉ với chi phí quá cao trong hiệu năng, các kiến ​​trúc phần cứng như vậy mới có thể được thực hiện để thực hiện các phép toán dấu phẩy động với độ chính xác thấp hơn và thay vì yêu cầu triển khai để mất cả hiệu suất và độ chính xác, C # cho phép sử dụng loại độ chính xác cao hơn cho tất cả các hoạt động của dấu phẩy động . Khác với việc cung cấp kết quả chính xác hơn, điều này hiếm khi có bất kỳ tác động có thể đo lường được. Tuy nhiên, trong các biểu thức có dạng x * y / z,

C # đảm bảo các hoạt động cung cấp mức độ chính xác ít nhất là ở cấp độ của IEEE 754, nhưng không nhất thiết phải chính xác như vậy. Đây không phải là một lỗi, đó là một tính năng cụ thể. Trình biên dịch Roslyn có quyền đánh giá biểu thức chính xác như chỉ định của IEEE 754 và trình biên dịch khác có quyền suy ra đó 2^32 + 77khi đưa vào uint.

Tôi xin lỗi vì câu trả lời đầu tiên sai lệch của tôi, nhưng ít nhất tất cả chúng ta đã học được điều gì hôm nay.


Sau đó, tôi đoán rằng chúng tôi có một lỗi trong trình biên dịch .NET Framework hiện tại (tôi chỉ thử trong VS 2019 để chắc chắn) :) Tôi đoán tôi sẽ thử xem có nơi nào để đăng nhập một lỗi hay không, mặc dù sửa một cái gì đó giống như vậy có thể có rất nhiều tác dụng phụ không mong muốn và có thể bị bỏ qua ...
Lukas

Tôi không nghĩ rằng việc đúc sớm cho int, điều đó sẽ gây ra nhiều vấn đề rõ ràng hơn trong RẤT NHIỀU trường hợp, tôi đoán trường hợp ở đây là trong hoạt động const, nó không đánh giá giá trị và truyền cho đến cuối cùng, nghĩa là là thay vì lưu trữ các giá trị trung gian trong số float, nó chỉ bỏ qua nó và thay thế nó trong mỗi biểu thức bằng chính biểu thức
jalsh

@jalsh Tôi không nghĩ tôi hiểu dự đoán của bạn. Nếu trình biên dịch chỉ đơn giản thay thế từng cái scalebằng giá trị float và sau đó đánh giá mọi thứ khác trong thời gian chạy, kết quả sẽ giống nhau. Bạn có thể xây dựng?
V0ldek

@ V0ldek, downvote là một lỗi, tôi đã chỉnh sửa câu trả lời của bạn để tôi có thể xóa nó :)
jalsh

Tôi đoán là nó không thực sự lưu trữ các giá trị trung gian trong phao, nó chỉ thay thế f bằng biểu thức đánh giá f mà không chuyển nó thành float
jalsh

0

Vấn đề ở đây là (như bạn có thể thấy trên các tài liệu ) rằng các giá trị float chỉ có thể có một cơ sở tối đa 2 ^ 24 . Vì vậy, khi bạn gán giá trị là 2 ^ 32 ( 64 * 2014 * 164 * 1024 = 2 ^ 6 * 2 ^ 10 * 2 ^ 6 * 2 ^ 10 = 2 ^ 32 ), thực tế là 2 ^ 24 * 2 ^ 8 , đó là 4294967000 . Thêm 7 sẽ chỉ được thêm vào phần bị cắt bớt bằng cách chuyển đổi sang ulong .

Nếu bạn thay đổi thành gấp đôi , có cơ sở là 2 ^ 53 , nó sẽ hoạt động cho những gì bạn muốn.

Đây có thể là một vấn đề thời gian chạy, nhưng trong trường hợp này, nó là một vấn đề thời gian biên dịch, bởi vì tất cả các giá trị là hằng số và sẽ được trình biên dịch đánh giá.


-2

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 UInt32loạ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 uncheckedtừ 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.


7.0 là gấp đôi, không phải là số float, hãy thử 7.0f, nó vẫn sẽ cung cấp cho bạn 0
jalsh

Có, nó phải là loại điểm nổi, không nổi. Cảm ơn đã sửa chữa.
bỏ học

Điều đó thay đổi toàn bộ quan điểm của vấn đề, khi xử lý gấp đôi độ chính xác bạn nhận được cao hơn rất nhiều và kết quả được giải thích trong câu trả lời của V0ldek thay đổi mạnh mẽ, bạn có thể thay đổi tỷ lệ thành gấp đôi và kiểm tra lại, kết quả sẽ giống nhau. ..
jalsh

Cuối cùng nó là vấn đề phức tạp hơn.
bỏ học

1
@jalsh Có, nhưng có một cờ trình biên dịch biến bối cảnh được kiểm tra ở mọi nơi. Bạn có thể muốn kiểm tra mọi thứ về an toàn, ngoại trừ một đường dẫn nóng nhất định cần tất cả các chu kỳ CPU có thể có.
V0ldek
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 cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.