diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index b76e5a04f5ce11..20ad904bc423a2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -258,7 +258,7 @@ public static explicit operator double(UInt128 value) // For values between 0 and ulong.MaxValue, we just use the existing conversion return (double)(value._lower); } - else if ((value._upper >> 24) == 0) // value < (2^104) + else if ((value._upper >> 40) == 0) // value < (2^104) { // For values greater than ulong.MaxValue but less than 2^104 this takes advantage // that we can represent both "halves" of the uint128 within the 52-bit mantissa of @@ -276,7 +276,7 @@ public static explicit operator double(UInt128 value) // lowest 24 bits and then or's them back to ensure rounding stays correct. double lower = BitConverter.UInt64BitsToDouble(TwoPow76Bits | ((ulong)(value >> 12) >> 12) | (value._lower & 0xFFFFFF)) - TwoPow76; - double upper = BitConverter.UInt64BitsToDouble(TwoPow128Bits | (ulong)(value >> 76)) - TwoPow128; + double upper = BitConverter.UInt64BitsToDouble(TwoPow128Bits | (value._upper >> 12)) - TwoPow128; return lower + upper; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.cs index ab94d43f1355aa..36b197524a0fec 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int128Tests.cs @@ -586,5 +586,34 @@ public static void BigMul(Int128 a, Int128 b, string result) Int128 upper = Int128.BigMul(a, b, out Int128 lower); Assert.Equal(result, $"{upper:X32}{lower:X32}"); } + + [Fact] + public static void ExplicitConversionToDouble_LargeValue() + { + // Value: 309485009821345068741558271 (approx 2^88) + Int128 value = Int128.Parse("309485009821345068741558271"); + double d = (double)value; + Assert.Equal(309485009821345068741558271.0, d); + + // Negative Value + Int128 valueNeg = -value; + double dNeg = (double)valueNeg; + Assert.Equal(-309485009821345068741558271.0, dNeg); + + // Value >= 2^104 + // The value is constructed as 2^104 + 2^24 + 1. + // This tests a value with a 1 at bit 104, a 1 at bit 24, and a 1 at bit 0. + // The lower bits (24 and 0) should contribute to the sticky bit calculation. + Int128 value2 = new(0x0100_0000_0000, 0x0100_0001); + double d2 = (double)value2; + double expected2 = 20282409603651670423947251286016.0; + Assert.Equal(expected2, d2); + + // Negative Value >= 2^104 + Int128 value2Neg = -value2; + double d2Neg = (double)value2Neg; + Assert.Equal(-expected2, d2Neg); + } } } + diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.cs index a5d8716a2ecf2b..634049dd2e667d 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.cs @@ -517,5 +517,26 @@ public static void BigMul(UInt128 a, UInt128 b, string result) UInt128 upper = UInt128.BigMul(a, b, out UInt128 lower); Assert.Equal(result, $"{upper:X32}{lower:X32}"); } + + [Fact] + public static void ExplicitConversionToDouble_LargeValue() + { + // Value: 309485009821345068741558271 (approx 2^88) + // This tests the path for values < 2^104 (after fix) or >= 2^88 (before fix) + UInt128 value = UInt128.Parse("309485009821345068741558271"); + double d = (double)value; + Assert.Equal(309485009821345068741558271.0, d); + + // Value >= 2^104 + // 2^104 = 20282409603651670423947251286016 + // The value is constructed as 2^104 + 2^24 + 1. + // This tests a value with a 1 at bit 104, a 1 at bit 24, and a 1 at bit 0. + // The lower bits (24 and 0) should contribute to the sticky bit calculation. + UInt128 value2 = new(0x0100_0000_0000, 0x0100_0001); + double d2 = (double)value2; + // Expected: 2^104. 2^24 is far below ULP (2^52). + double expected2 = 20282409603651670423947251286016.0; + Assert.Equal(expected2, d2); + } } }