diff --git a/src/MissingValues/Int256.Conversions.cs b/src/MissingValues/Int256.Conversions.cs
index f995ed7..5ace6c6 100644
--- a/src/MissingValues/Int256.Conversions.cs
+++ b/src/MissingValues/Int256.Conversions.cs
@@ -645,6 +645,34 @@ public static explicit operator decimal(in Int256 value)
}
return (decimal)(double)(UInt256)(value);
}
+
+#if NET11_0_OR_GREATER
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Decimal32(in Int256 value)
+ {
+ return BitHelper.ConvertToDecimalN(in value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Decimal64(in Int256 value)
+ {
+ return BitHelper.ConvertToDecimalN(in value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Decimal128(in Int256 value)
+ {
+ return BitHelper.ConvertToDecimalN(in value);
+ }
+#endif
+
///
/// Explicitly converts a value to a .
///
@@ -872,6 +900,63 @@ public static explicit operator checked Int256(BigInteger value)
/// The value to convert.
/// is outside the range of .
public static explicit operator checked Int256(decimal value) => checked((Int256)(double)value);
+
+#if NET11_0_OR_GREATER
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Int256(Decimal32 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ /// is outside the range of .
+ public static explicit operator checked Int256(Decimal32 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value, true);
+ }
+
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Int256(Decimal64 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ /// is outside the range of .
+ public static explicit operator checked Int256(Decimal64 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value, true);
+ }
+
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Int256(Decimal128 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ /// is outside the range of .
+ public static explicit operator checked Int256(Decimal128 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value, true);
+ }
+#endif
+
///
/// Explicitly converts a value to a .
///
diff --git a/src/MissingValues/Int512.Conversions.cs b/src/MissingValues/Int512.Conversions.cs
index e24c830..c5f829a 100644
--- a/src/MissingValues/Int512.Conversions.cs
+++ b/src/MissingValues/Int512.Conversions.cs
@@ -658,6 +658,34 @@ public static explicit operator decimal(in Int512 value)
}
return (decimal)(UInt512)(value);
}
+
+#if NET11_0_OR_GREATER
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Decimal32(in Int512 value)
+ {
+ return BitHelper.ConvertToDecimalN(in value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Decimal64(in Int512 value)
+ {
+ return BitHelper.ConvertToDecimalN(in value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Decimal128(in Int512 value)
+ {
+ return BitHelper.ConvertToDecimalN(in value);
+ }
+#endif
+
///
/// Explicitly converts a value to a .
///
@@ -894,6 +922,63 @@ public static explicit operator checked Int512(BigInteger value)
/// The value to convert.
/// is outside the range of .
public static explicit operator checked Int512(decimal value) => checked((Int512)(double)value);
+
+#if NET11_0_OR_GREATER
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Int512(Decimal32 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ /// is outside the range of .
+ public static explicit operator checked Int512(Decimal32 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value, true);
+ }
+
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Int512(Decimal64 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ /// is outside the range of .
+ public static explicit operator checked Int512(Decimal64 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value, true);
+ }
+
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Int512(Decimal128 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ /// is outside the range of .
+ public static explicit operator checked Int512(Decimal128 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value, true);
+ }
+#endif
+
///
/// Explicitly converts a value to a .
///
diff --git a/src/MissingValues/Internals/BitHelper.Ieee.cs b/src/MissingValues/Internals/BitHelper.Ieee.cs
index 5489ae4..c673c09 100644
--- a/src/MissingValues/Internals/BitHelper.Ieee.cs
+++ b/src/MissingValues/Internals/BitHelper.Ieee.cs
@@ -1,4 +1,5 @@
-using System.Diagnostics;
+using System.Buffers.Binary;
+using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using MissingValues.Primitives;
@@ -767,6 +768,141 @@ internal static void FastFloor(in Quad x, out Quad ai, out Quad ar)
ar = x - ai;
}
+#if NET11_0_OR_GREATER
+ private static void GetExponentAndSignificand(T value, out int exponent, out UInt512 significand)
+ where T : IDecimalFloatingPointIeee754
+ {
+ Span exponentBuffer = stackalloc byte[8];
+ Span significandBuffer = stackalloc byte[64];
+
+ value.TryWriteExponentLittleEndian(exponentBuffer, out int written);
+ Debug.Assert(written <= 8);
+ value.TryWriteSignificandLittleEndian(significandBuffer, out written);
+ Debug.Assert(written <= 64);
+
+ exponent = BinaryPrimitives.ReadInt32LittleEndian(exponentBuffer);
+ significand = BinaryOperations.ReadUInt512LittleEndian(significandBuffer);
+ }
+
+ internal static TInteger ConvertFromDecimalN(TDecimal value, bool isChecked = false)
+ where TInteger : IBigInteger, IMinMaxValue
+ where TDecimal : IDecimalFloatingPointIeee754
+ {
+ if (TDecimal.IsNaN(value))
+ {
+ if (isChecked)
+ {
+ Thrower.IntegerOverflow();
+ }
+ return TInteger.Zero;
+ }
+
+ bool isNegative = TDecimal.IsNegative(value);
+
+ if (TDecimal.IsInfinity(value))
+ {
+ if (isChecked)
+ {
+ Thrower.IntegerOverflow();
+ }
+ return isNegative ? TInteger.MinValue : TInteger.MaxValue;
+ }
+
+ GetExponentAndSignificand(value, out var exponent, out var significand);
+
+ if (significand == UInt512.Zero)
+ {
+ return TInteger.Zero;
+ }
+
+ UInt512 magnitude;
+ bool exceedsUInt512 = false;
+ if (exponent >= 0)
+ {
+ // magnitude = significand * 10^exponent. UInt512 holds at most 155 digits, so any exponent that
+ // would push the product past that bound overflows every integer type and saturates/throws.
+ UInt512 maxValueBy10 = UInt512.MaxValue / 10;
+ for (int i = 0; i < exponent && !exceedsUInt512; i++)
+ {
+ if (significand > maxValueBy10)
+ {
+ exceedsUInt512 = true;
+ break;
+ }
+ significand *= 10;
+ }
+
+ magnitude = exceedsUInt512 ? UInt512.Zero : significand;
+ }
+ else
+ {
+ // magnitude = significand / 10^(-exponent), truncated toward zero. Once the divisor has more
+ // digits than the significand the quotient is zero.
+ int drop = -exponent;
+
+ for (int i = 0; i < drop && significand != UInt512.Zero; i++)
+ {
+ significand /= 10;
+ }
+
+ magnitude = significand;
+ }
+
+ bool isUnsigned = TInteger.IsZero(TInteger.MinValue);
+ UInt512 maxMagnitude = UInt512.CreateTruncating(TInteger.MaxValue);
+
+ UInt512 minMagnitude = isUnsigned ? UInt512.Zero : maxMagnitude + UInt512.One;
+
+ if (!isNegative)
+ {
+ if (exceedsUInt512 || (magnitude > maxMagnitude))
+ {
+ if (isChecked)
+ {
+ Thrower.IntegerOverflow();
+ }
+ return TInteger.MaxValue;
+ }
+
+ return TInteger.CreateTruncating(magnitude);
+ }
+
+ // Negative magnitude.
+ if (isUnsigned)
+ {
+ if (isChecked && (magnitude != UInt512.Zero))
+ {
+ Thrower.IntegerOverflow();
+ }
+ return TInteger.Zero;
+ }
+
+ if (exceedsUInt512 || (magnitude > minMagnitude))
+ {
+ if (isChecked)
+ {
+ Thrower.IntegerOverflow();
+ }
+ return TInteger.MinValue;
+ }
+
+ if (magnitude == minMagnitude)
+ {
+ return TInteger.MinValue;
+ }
+
+ return TInteger.Zero - TInteger.CreateTruncating(magnitude);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static TDecimal ConvertToDecimalN(in TInteger value)
+ where TDecimal : IDecimalFloatingPointIeee754
+ where TInteger : IBigInteger, IMinMaxValue
+ {
+ throw new NotImplementedException();
+ }
+#endif
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static UInt128 AddQuadBits(UInt128 uiA, UInt128 uiB, bool signZ)
{
diff --git a/src/MissingValues/UInt256.Conversions.cs b/src/MissingValues/UInt256.Conversions.cs
index ab5eb10..fd5ef67 100644
--- a/src/MissingValues/UInt256.Conversions.cs
+++ b/src/MissingValues/UInt256.Conversions.cs
@@ -604,6 +604,34 @@ public static explicit operator decimal(in UInt256 value)
return (decimal)value.Lower;
}
+
+#if NET11_0_OR_GREATER
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Decimal32(in UInt256 value)
+ {
+ return BitHelper.ConvertToDecimalN(in value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Decimal64(in UInt256 value)
+ {
+ return BitHelper.ConvertToDecimalN(in value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Decimal128(in UInt256 value)
+ {
+ return BitHelper.ConvertToDecimalN(in value);
+ }
+#endif
+
///
/// Explicitly converts a value to a .
///
@@ -781,6 +809,63 @@ public static explicit operator checked UInt256(double value)
return ToUInt256(value);
}
+
+#if NET11_0_OR_GREATER
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator UInt256(Decimal32 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ /// is outside the range of .
+ public static explicit operator checked UInt256(Decimal32 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value, true);
+ }
+
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator UInt256(Decimal64 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ /// is outside the range of .
+ public static explicit operator checked UInt256(Decimal64 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value, true);
+ }
+
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator UInt256(Decimal128 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ /// is outside the range of .
+ public static explicit operator checked UInt256(Decimal128 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value, true);
+ }
+#endif
+
///
/// Explicitly converts a value to a .
///
diff --git a/src/MissingValues/UInt512.Conversions.cs b/src/MissingValues/UInt512.Conversions.cs
index e571c7f..ceb5816 100644
--- a/src/MissingValues/UInt512.Conversions.cs
+++ b/src/MissingValues/UInt512.Conversions.cs
@@ -618,6 +618,34 @@ public static explicit operator decimal(in UInt512 value)
return (decimal)value.Lower;
}
+
+#if NET11_0_OR_GREATER
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Decimal32(in UInt512 value)
+ {
+ return BitHelper.ConvertToDecimalN(in value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Decimal64(in UInt512 value)
+ {
+ return BitHelper.ConvertToDecimalN(in value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator Decimal128(in UInt512 value)
+ {
+ return BitHelper.ConvertToDecimalN(in value);
+ }
+#endif
+
///
/// Explicitly converts a value to a .
///
@@ -1104,6 +1132,63 @@ public static explicit operator checked UInt512(double value)
return ToUInt512(value);
}
+
+#if NET11_0_OR_GREATER
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator UInt512(Decimal32 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ /// is outside the range of .
+ public static explicit operator checked UInt512(Decimal32 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value, true);
+ }
+
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator UInt512(Decimal64 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ /// is outside the range of .
+ public static explicit operator checked UInt512(Decimal64 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value, true);
+ }
+
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ public static explicit operator UInt512(Decimal128 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value);
+ }
+ ///
+ /// Explicitly converts a value to a .
+ ///
+ /// The value to convert.
+ /// is outside the range of .
+ public static explicit operator checked UInt512(Decimal128 value)
+ {
+ return BitHelper.ConvertFromDecimalN(value, true);
+ }
+#endif
+
///
/// Explicitly converts a value to a .
///
diff --git a/tests/MissingValues.Tests/Extensions/OctoExtensions.cs b/tests/MissingValues.Tests/Extensions/OctoExtensions.cs
index fa7c327..2990d86 100644
--- a/tests/MissingValues.Tests/Extensions/OctoExtensions.cs
+++ b/tests/MissingValues.Tests/Extensions/OctoExtensions.cs
@@ -65,6 +65,13 @@ public static class OctoExtensions
public static Octo NFloatMinValue => NFloat.Size == 8 ? Octo.DoubleMinValue : Octo.SingleMinValue;
public static Octo QuadMaxValue => Values.CreateFloat(0x43FF_EFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF, 0xF000_0000_0000_0000, 0x0000_0000_0000_0000);
public static Octo QuadMinValue => Values.CreateFloat(0xC3FF_EFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF, 0xF000_0000_0000_0000, 0x0000_0000_0000_0000);
+
+ public static Octo Decimal32MaxValue => Values.CreateFloat(0x4017_65C0_1040_09E0, 0x12E0_0300_08A0_0380, 0x1AED_6792_CCAD_29A9, 0xD27D_9160_3B50_BF5D);
+ public static Octo Decimal32MinValue => Values.CreateFloat(0xC017_65C0_1040_09E0, 0x12E0_0300_08A0_0380, 0x1AED_6792_CCAD_29A9, 0xD27D_9160_3B50_BF5D);
+ public static Octo Decimal64MaxValue => Values.CreateFloat(0x404F_DEBE_EB7A_9B56, 0xD9B6_0E91_DC03_AB30, 0xAFC7_EA72_4341_EA33, 0x2994_DBDE_1B58_8CA4);
+ public static Octo Decimal64MinValue => Values.CreateFloat(0xC04F_DEBE_EB7A_9B56, 0xD9B6_0E91_DC03_AB30, 0xAFC7_EA72_4341_EA33, 0x2994_DBDE_1B58_8CA4);
+ public static Octo Decimal128MaxValue => Values.CreateFloat(0x44FF_4300_0080_3A00, 0x1200_2900_0880_6B80, 0x4490_2FF5_3A96_BC5, 0xC2D6A_4594_3117_1A7);
+ public static Octo Decimal128MinValue => Values.CreateFloat(0xC4FF_4300_0080_3A00, 0x1200_2900_0880_6B80, 0x4490_2FF5_3A96_BC5, 0xC2D6A_4594_3117_1A7);
public static Octo Delta => Values.CreateFloat(0x400E_B000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000);
}
diff --git a/tests/MissingValues.Tests/Extensions/QuadExtensions.cs b/tests/MissingValues.Tests/Extensions/QuadExtensions.cs
index bc46840..9b0047f 100644
--- a/tests/MissingValues.Tests/Extensions/QuadExtensions.cs
+++ b/tests/MissingValues.Tests/Extensions/QuadExtensions.cs
@@ -64,6 +64,11 @@ public static class QuadExtensions
public static Quad DoubleMinValue => Values.CreateFloat(0xC3FE_FFFF_FFFF_FFFF, 0xF000_0000_0000_0000);
public static Quad NFloatMaxValue => NFloat.Size == 8 ? Quad.DoubleMaxValue : Quad.SingleMaxValue;
public static Quad NFloatMinValue => NFloat.Size == 8 ? Quad.DoubleMinValue : Quad.SingleMinValue;
+
+ public static Quad Decimal32MaxValue => Values.CreateFloat(0x4141_2BA0_93E5_C611, 0x4735_DACF_2599_5A53);
+ public static Quad Decimal32MinValue => Values.CreateFloat(0xC141_2BA0_93E5_C611, 0x4735_DACF_2599_5A53);
+ public static Quad Decimal64MaxValue => Values.CreateFloat(0x44FD_EBEE_B7A9_B56D, 0x9B60_E91D_C03A_B30B);
+ public static Quad Decimal64MinValue => Values.CreateFloat(0xC4FD_EBEE_B7A9_B56D, 0x9B60_E91D_C03A_B30B);
public static Quad Delta => Values.CreateFloat(0x406F_0000_0000_0000, 0x0000_0000_0000_0000);
}