Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
316f66c
Implement unary plus/negation for Decimal32/64/128
tannergooding Jul 10, 2026
fae0d5c
Implement addition/subtraction for Decimal32/64/128
tannergooding Jul 10, 2026
b25a664
Implement equality/inequality for Decimal32/64/128
tannergooding Jul 10, 2026
6c5b48f
Implement relational comparisons for Decimal32/64/128
tannergooding Jul 10, 2026
a7261e1
Implement multiplication for Decimal32/64/128
tannergooding Jul 10, 2026
fe33fe0
Implement division for Decimal32/64/128
tannergooding Jul 10, 2026
dcd5fb2
Implement increment and decrement for Decimal32/64/128
tannergooding Jul 10, 2026
98b99a5
Rewrite Decimal32/64/128 arithmetic to word-level integer operations
tannergooding Jul 10, 2026
6fde6a7
Regenerate System.Runtime ref for Decimal32/64/128 via GenAPI
tannergooding Jul 10, 2026
72489df
Note reciprocal-multiply table optimization opportunity in WideDivide…
tannergooding Jul 10, 2026
2bbabff
Add Intel Decimal Floating-Point Math Library notice to THIRD-PARTY-N…
tannergooding Jul 10, 2026
20bcd99
Merge remote-tracking branch 'dotnet/main' into tannergooding-decimal…
tannergooding Jul 10, 2026
c15767a
Treat non-canonical finite decimal encodings as zero when unpacking
tannergooding Jul 10, 2026
12a7668
Skip WideDigitCount when the exponent already overflows to infinity
tannergooding Jul 10, 2026
eafeb1f
Use the Power10 table for in-range operand alignment in AddDecimalIee…
tannergooding Jul 10, 2026
1abea0a
Make increment/decrement operator test comments match their dataset
tannergooding Jul 10, 2026
b59784a
Fix equality comment and unused parameter in decimal operator tests
tannergooding Jul 10, 2026
fc9e4b2
Widen decimal wide-arithmetic helpers to native integers for the 32-b…
tannergooding Jul 11, 2026
a4eeac3
Propagate decimal NaN payloads and match the Intel invalid-operation …
tannergooding Jul 11, 2026
12e7997
Cross-validate decimal operators against the Intel reference vectors
tannergooding Jul 11, 2026
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
33 changes: 33 additions & 0 deletions THIRD-PARTY-NOTICES.TXT
Original file line number Diff line number Diff line change
Expand Up @@ -1664,3 +1664,36 @@ Available at https://github.com/C2SP/wycheproof/blob/2fb7240f471b70bb3aa5cc9b071
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

License notice for Intel Decimal Floating-Point Math Library
-------------------------------

https://www.intel.com/content/www/us/en/developer/articles/tool/intel-decimal-floating-point-math-library.html

Copyright (c) 2007-2025, Intel Corp.

All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Intel Corporation nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Large diffs are not rendered by default.

124 changes: 124 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public readonly struct Decimal128
// which stores zero with the biased exponent rather than the minimum exponent.
private static UInt128 ZeroValue => new UInt128(0x3040_0000_0000_0000, 0);
private static UInt128 NegativeZeroValue => new UInt128(0xB040_0000_0000_0000, 0);
// One (+1 * 10^0) shares the biased exponent of canonical zero with a coefficient of one.
private static UInt128 OneValue => new UInt128(0x3040_0000_0000_0000, 1);
private static UInt128 QuietNaNValue => new UInt128(0xFC00_0000_0000_0000, 0);

private const ulong SignMaskUpper = 0x8000_0000_0000_0000;
Expand Down Expand Up @@ -246,6 +248,128 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin
return Number.FormatDecimalIeee754<Decimal128, UInt128>(new UInt128(_upper, _lower), format, NumberFormatInfo.GetInstance(provider));
}

/// <summary>Computes the unary plus of a value.</summary>
/// <param name="value">The value for which to compute the unary plus.</param>
/// <returns><paramref name="value" /> unchanged.</returns>
public static Decimal128 operator +(Decimal128 value) => value;

/// <summary>Computes the unary negation of a value.</summary>
/// <param name="value">The value for which to compute the unary negation.</param>
/// <returns>The unary negation of <paramref name="value" />.</returns>
public static Decimal128 operator -(Decimal128 value) => new Decimal128(value._upper ^ SignMaskUpper, value._lower);

/// <summary>Increments a value.</summary>
/// <param name="value">The value to increment.</param>
/// <returns>The result of incrementing <paramref name="value" /> by one.</returns>
public static Decimal128 operator ++(Decimal128 value)
{
UInt128 result = Number.AddDecimalIeee754<Decimal128, UInt128>(new UInt128(value._upper, value._lower), OneValue);
return new Decimal128(result);
}

/// <summary>Decrements a value.</summary>
/// <param name="value">The value to decrement.</param>
/// <returns>The result of decrementing <paramref name="value" /> by one.</returns>
public static Decimal128 operator --(Decimal128 value)
{
UInt128 result = Number.SubtractDecimalIeee754<Decimal128, UInt128>(new UInt128(value._upper, value._lower), OneValue);
return new Decimal128(result);
}

/// <summary>Adds two values together to compute their sum.</summary>
/// <param name="left">The value to which <paramref name="right" /> is added.</param>
/// <param name="right">The value which is added to <paramref name="left" />.</param>
/// <returns>The sum of <paramref name="left" /> and <paramref name="right" />.</returns>
public static Decimal128 operator +(Decimal128 left, Decimal128 right)
{
UInt128 result = Number.AddDecimalIeee754<Decimal128, UInt128>(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower));
return new Decimal128(result);
}

/// <summary>Subtracts two values to compute their difference.</summary>
/// <param name="left">The value from which <paramref name="right" /> is subtracted.</param>
/// <param name="right">The value which is subtracted from <paramref name="left" />.</param>
/// <returns>The difference of <paramref name="right" /> subtracted from <paramref name="left" />.</returns>
public static Decimal128 operator -(Decimal128 left, Decimal128 right)
{
UInt128 result = Number.SubtractDecimalIeee754<Decimal128, UInt128>(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower));
return new Decimal128(result);
}

/// <summary>Multiplies two values together to compute their product.</summary>
/// <param name="left">The value which <paramref name="right" /> multiplies.</param>
/// <param name="right">The value which multiplies <paramref name="left" />.</param>
/// <returns>The product of <paramref name="left" /> and <paramref name="right" />.</returns>
public static Decimal128 operator *(Decimal128 left, Decimal128 right)
{
UInt128 result = Number.MultiplyDecimalIeee754<Decimal128, UInt128>(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower));
return new Decimal128(result);
}

/// <summary>Divides two values together to compute their quotient.</summary>
/// <param name="left">The value which <paramref name="right" /> divides.</param>
/// <param name="right">The value which divides <paramref name="left" />.</param>
/// <returns>The quotient of <paramref name="left" /> divided by <paramref name="right" />.</returns>
public static Decimal128 operator /(Decimal128 left, Decimal128 right)
{
UInt128 result = Number.DivideDecimalIeee754<Decimal128, UInt128>(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower));
return new Decimal128(result);
}

/// <summary>Compares two values to determine equality.</summary>
/// <param name="left">The value to compare with <paramref name="right" />.</param>
/// <param name="right">The value to compare with <paramref name="left" />.</param>
/// <returns><c>true</c> if <paramref name="left" /> is equal to <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator ==(Decimal128 left, Decimal128 right)
{
return Number.EqualsDecimalIeee754<Decimal128, UInt128>(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower));
}

/// <summary>Compares two values to determine inequality.</summary>
/// <param name="left">The value to compare with <paramref name="right" />.</param>
/// <param name="right">The value to compare with <paramref name="left" />.</param>
/// <returns><c>true</c> if <paramref name="left" /> is not equal to <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator !=(Decimal128 left, Decimal128 right)
{
return !(left == right);
}

/// <summary>Compares two values to determine which is less.</summary>
/// <param name="left">The value to compare with <paramref name="right" />.</param>
/// <param name="right">The value to compare with <paramref name="left" />.</param>
/// <returns><c>true</c> if <paramref name="left" /> is less than <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator <(Decimal128 left, Decimal128 right)
{
return Number.LessThanDecimalIeee754<Decimal128, UInt128>(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower));
}

/// <summary>Compares two values to determine which is greater.</summary>
/// <param name="left">The value to compare with <paramref name="right" />.</param>
/// <param name="right">The value to compare with <paramref name="left" />.</param>
/// <returns><c>true</c> if <paramref name="left" /> is greater than <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator >(Decimal128 left, Decimal128 right)
{
return Number.GreaterThanDecimalIeee754<Decimal128, UInt128>(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower));
}

/// <summary>Compares two values to determine which is less or equal.</summary>
/// <param name="left">The value to compare with <paramref name="right" />.</param>
/// <param name="right">The value to compare with <paramref name="left" />.</param>
/// <returns><c>true</c> if <paramref name="left" /> is less than or equal to <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator <=(Decimal128 left, Decimal128 right)
{
return Number.LessThanOrEqualDecimalIeee754<Decimal128, UInt128>(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower));
}

/// <summary>Compares two values to determine which is greater or equal.</summary>
/// <param name="left">The value to compare with <paramref name="right" />.</param>
/// <param name="right">The value to compare with <paramref name="left" />.</param>
/// <returns><c>true</c> if <paramref name="left" /> is greater than or equal to <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator >=(Decimal128 left, Decimal128 right)
{
return Number.GreaterThanOrEqualDecimalIeee754<Decimal128, UInt128>(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower));
}

//
// INumberBase
//
Expand Down
118 changes: 118 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ internal Decimal32(uint value)
// which stores zero with the biased exponent rather than the minimum exponent.
private const uint ZeroValue = 0x3280_0000;
private const uint NegativeZeroValue = 0xB280_0000;
// One (+1 * 10^0) shares the biased exponent of canonical zero with a coefficient of one.
private const uint OneValue = ZeroValue | 0x1;
private const uint QuietNaNValue = 0xFC00_0000;
private const uint G0G1Mask = 0x6000_0000;
private const uint SignMask = 0x8000_0000;
Expand Down Expand Up @@ -252,6 +254,122 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin
return Number.FormatDecimalIeee754<Decimal32, uint>(_value, format, NumberFormatInfo.GetInstance(provider));
}

/// <summary>Computes the unary plus of a value.</summary>
/// <param name="value">The value for which to compute the unary plus.</param>
/// <returns><paramref name="value" /> unchanged.</returns>
public static Decimal32 operator +(Decimal32 value) => value;

/// <summary>Computes the unary negation of a value.</summary>
/// <param name="value">The value for which to compute the unary negation.</param>
/// <returns>The unary negation of <paramref name="value" />.</returns>
public static Decimal32 operator -(Decimal32 value) => new Decimal32(value._value ^ SignMask);

/// <summary>Increments a value.</summary>
/// <param name="value">The value to increment.</param>
/// <returns>The result of incrementing <paramref name="value" /> by one.</returns>
public static Decimal32 operator ++(Decimal32 value)
{
return new Decimal32(Number.AddDecimalIeee754<Decimal32, uint>(value._value, OneValue));
}

/// <summary>Decrements a value.</summary>
/// <param name="value">The value to decrement.</param>
/// <returns>The result of decrementing <paramref name="value" /> by one.</returns>
public static Decimal32 operator --(Decimal32 value)
{
return new Decimal32(Number.SubtractDecimalIeee754<Decimal32, uint>(value._value, OneValue));
}

/// <summary>Adds two values together to compute their sum.</summary>
/// <param name="left">The value to which <paramref name="right" /> is added.</param>
/// <param name="right">The value which is added to <paramref name="left" />.</param>
/// <returns>The sum of <paramref name="left" /> and <paramref name="right" />.</returns>
public static Decimal32 operator +(Decimal32 left, Decimal32 right)
{
return new Decimal32(Number.AddDecimalIeee754<Decimal32, uint>(left._value, right._value));
}

/// <summary>Subtracts two values to compute their difference.</summary>
/// <param name="left">The value from which <paramref name="right" /> is subtracted.</param>
/// <param name="right">The value which is subtracted from <paramref name="left" />.</param>
/// <returns>The difference of <paramref name="right" /> subtracted from <paramref name="left" />.</returns>
public static Decimal32 operator -(Decimal32 left, Decimal32 right)
{
return new Decimal32(Number.SubtractDecimalIeee754<Decimal32, uint>(left._value, right._value));
}

/// <summary>Multiplies two values together to compute their product.</summary>
/// <param name="left">The value which <paramref name="right" /> multiplies.</param>
/// <param name="right">The value which multiplies <paramref name="left" />.</param>
/// <returns>The product of <paramref name="left" /> and <paramref name="right" />.</returns>
public static Decimal32 operator *(Decimal32 left, Decimal32 right)
{
return new Decimal32(Number.MultiplyDecimalIeee754<Decimal32, uint>(left._value, right._value));
}

/// <summary>Divides two values together to compute their quotient.</summary>
/// <param name="left">The value which <paramref name="right" /> divides.</param>
/// <param name="right">The value which divides <paramref name="left" />.</param>
/// <returns>The quotient of <paramref name="left" /> divided by <paramref name="right" />.</returns>
public static Decimal32 operator /(Decimal32 left, Decimal32 right)
{
return new Decimal32(Number.DivideDecimalIeee754<Decimal32, uint>(left._value, right._value));
}

/// <summary>Compares two values to determine equality.</summary>
/// <param name="left">The value to compare with <paramref name="right" />.</param>
/// <param name="right">The value to compare with <paramref name="left" />.</param>
/// <returns><c>true</c> if <paramref name="left" /> is equal to <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator ==(Decimal32 left, Decimal32 right)
{
return Number.EqualsDecimalIeee754<Decimal32, uint>(left._value, right._value);
}

/// <summary>Compares two values to determine inequality.</summary>
/// <param name="left">The value to compare with <paramref name="right" />.</param>
/// <param name="right">The value to compare with <paramref name="left" />.</param>
/// <returns><c>true</c> if <paramref name="left" /> is not equal to <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator !=(Decimal32 left, Decimal32 right)
{
return !(left == right);
}

/// <summary>Compares two values to determine which is less.</summary>
/// <param name="left">The value to compare with <paramref name="right" />.</param>
/// <param name="right">The value to compare with <paramref name="left" />.</param>
/// <returns><c>true</c> if <paramref name="left" /> is less than <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator <(Decimal32 left, Decimal32 right)
{
return Number.LessThanDecimalIeee754<Decimal32, uint>(left._value, right._value);
}

/// <summary>Compares two values to determine which is greater.</summary>
/// <param name="left">The value to compare with <paramref name="right" />.</param>
/// <param name="right">The value to compare with <paramref name="left" />.</param>
/// <returns><c>true</c> if <paramref name="left" /> is greater than <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator >(Decimal32 left, Decimal32 right)
{
return Number.GreaterThanDecimalIeee754<Decimal32, uint>(left._value, right._value);
}

/// <summary>Compares two values to determine which is less or equal.</summary>
/// <param name="left">The value to compare with <paramref name="right" />.</param>
/// <param name="right">The value to compare with <paramref name="left" />.</param>
/// <returns><c>true</c> if <paramref name="left" /> is less than or equal to <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator <=(Decimal32 left, Decimal32 right)
{
return Number.LessThanOrEqualDecimalIeee754<Decimal32, uint>(left._value, right._value);
}

/// <summary>Compares two values to determine which is greater or equal.</summary>
/// <param name="left">The value to compare with <paramref name="right" />.</param>
/// <param name="right">The value to compare with <paramref name="left" />.</param>
/// <returns><c>true</c> if <paramref name="left" /> is greater than or equal to <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator >=(Decimal32 left, Decimal32 right)
{
return Number.GreaterThanOrEqualDecimalIeee754<Decimal32, uint>(left._value, right._value);
}

//
// INumberBase
//
Expand Down
Loading