Conversions from UInt64 to Double for values greater than long.MaxValue are currently incorrect. The same issue may exist for UInt32 to Double for values greater than int.MaxValue but I have not confirmed.
The simple repro case is detailed here: https://github.com/dotnet/csharplang/discussions/4074
using System;
namespace App
{
internal static class Program
{
internal static void Main()
{
/// Displays 'True'.
Console.WriteLine(10648738977740919977d != 10648738977740919977);
}
}
}
You can see approximately what the runtime generates by looking at: https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBDAzgWzQBMQBqAHwAEAmARgFgAoSgZgAIa2BhNgbybaCO7IhACuwADYw2AWQAUYyRAB2AczYIAlHwFD9lAOyaA3HsEBfJhaA===
public double M(ulong x) {
return x;
}
Which results in:
L0000: vzeroupper
L0003: vxorps xmm0, xmm0, xmm0
L0007: vcvtsi2sd xmm0, xmm0, rdx
L000c: test rdx, rdx
L000f: jge short L0019
L0011: vaddsd xmm0, xmm0, [C.M(UInt64)]
L0019: ret
The constant used by L0011 appears to be 1.8446744073709552E+19 (0x43f0000000000000)
This looks to be a "known" issue and was done for "back compat": https://github.com/dotnet/runtime/blob/master/src/coreclr/src/jit/codegenxarch.cpp#L6435-L6468
Clang vs MSVC generate different code with the MSVC variant being similar to our own, but which produces a correct result: https://godbolt.org/z/P7j6n8
Given this is .NET Core and given this is a correctness issue; I would say we take the breaking change and ensure we generate the correct IEEE compliant conversion.
Most helpful comment
Given this is .NET Core and given this is a correctness issue; I would say we take the breaking change and ensure we generate the correct IEEE compliant conversion.