From d2498356a59d348fd674ea127461cd93e6ebc41b Mon Sep 17 00:00:00 2001 From: crookseta Date: Sun, 16 Aug 2026 18:12:40 -0500 Subject: [PATCH 1/2] -Added cast from Decimal(32/64/128) to big integers. --- src/MissingValues/Int256.Conversions.cs | 85 +++++++++++ src/MissingValues/Int512.Conversions.cs | 85 +++++++++++ src/MissingValues/Internals/BitHelper.Ieee.cs | 138 +++++++++++++++++- src/MissingValues/UInt256.Conversions.cs | 85 +++++++++++ src/MissingValues/UInt512.Conversions.cs | 85 +++++++++++ 5 files changed, 477 insertions(+), 1 deletion(-) diff --git a/src/MissingValues/Int256.Conversions.cs b/src/MissingValues/Int256.Conversions.cs index 7792a1f..9b172d1 100644 --- a/src/MissingValues/Int256.Conversions.cs +++ b/src/MissingValues/Int256.Conversions.cs @@ -638,6 +638,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 . /// @@ -852,6 +880,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 e58a968..15eb52f 100644 --- a/src/MissingValues/Int512.Conversions.cs +++ b/src/MissingValues/Int512.Conversions.cs @@ -651,6 +651,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 . /// @@ -874,6 +902,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 5ebe04c..d0f32d6 100644 --- a/src/MissingValues/UInt256.Conversions.cs +++ b/src/MissingValues/UInt256.Conversions.cs @@ -590,6 +590,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 . /// @@ -759,6 +787,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 ec97928..00adf20 100644 --- a/src/MissingValues/UInt512.Conversions.cs +++ b/src/MissingValues/UInt512.Conversions.cs @@ -601,6 +601,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 . /// @@ -1044,6 +1072,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 . /// From 2c72a60a30ad919716ebb807bbc5e6612be97f17 Mon Sep 17 00:00:00 2001 From: crookseta Date: Sun, 16 Aug 2026 18:13:22 -0500 Subject: [PATCH 2/2] Added IEEE Decimal constant values for testing Quad and Octo. --- tests/MissingValues.Tests/Extensions/OctoExtensions.cs | 7 +++++++ tests/MissingValues.Tests/Extensions/QuadExtensions.cs | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/tests/MissingValues.Tests/Extensions/OctoExtensions.cs b/tests/MissingValues.Tests/Extensions/OctoExtensions.cs index ee3da17..828ea38 100644 --- a/tests/MissingValues.Tests/Extensions/OctoExtensions.cs +++ b/tests/MissingValues.Tests/Extensions/OctoExtensions.cs @@ -58,6 +58,13 @@ public static class OctoExtensions public static Octo DoubleMinValue => Values.CreateFloat(0xC03F_EFFF_FFFF_FFFF, 0xFF00000000000000, 0x0000_0000_0000_0000, 0x0000_0000_0000_0000); 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 d54a029..1429b39 100644 --- a/tests/MissingValues.Tests/Extensions/QuadExtensions.cs +++ b/tests/MissingValues.Tests/Extensions/QuadExtensions.cs @@ -57,6 +57,11 @@ public static class QuadExtensions public static Quad SingleMinValue => Values.CreateFloat(0xC07E_FFFF_FE00_0000, 0x0000_0000_0000_0000); public static Quad DoubleMaxValue => Values.CreateFloat(0x43FE_FFFF_FFFF_FFFF, 0xF000_0000_0000_0000); public static Quad DoubleMinValue => Values.CreateFloat(0xC3FE_FFFF_FFFF_FFFF, 0xF000_0000_0000_0000); + + 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); }