Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/libraries/System.Private.CoreLib/src/System/UInt128.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Loading