Skip to content

Commit 8d67bbd

Browse files
tannergoodingCopilot
authored andcommitted
Fix decimal to/from floating-point conversions to round correctly (#130566)
Fixes #72125 and Fixes #72135 — both directions of decimal ↔ floating-point conversion rounded through intermediate floating-point steps and lost precision. - `decimal → double/float` combined `(double)Low64 + (double)High * 2^64` and then divided by `10^scale`, rounding several times (e.g. `10000000000000.099609375m` produced the wrong double). - `double/float → decimal` assumed the source carried only 15 (double) or 7 (float) significant digits and truncated the rest (e.g. `(decimal)1.23` gave `1.23` instead of `1.2299999999999999822364316060`). Both are reimplemented as correctly-rounded integer algorithms so a round-trip matches `decimal.Parse(value.ToString("G99"))`: - **`decimal → floating-point`** reuses the parser's own correctly-rounded fast paths — Clinger (`(double)mantissa / Pow10DoubleTable[scale]`) and Eisel-Lemire (`Number.ComputeFloat`) — falling back to an exact 128-bit division only for mantissas wider than 64 bits and the rare case Eisel-Lemire cannot decide. `Number.Pow10DoubleTable` was made `internal` to reuse it. - **`floating-point → decimal`** decomposes the value into an odd `significand * 2^exponent` and rounds `significand * 5^k` once to fit the 96-bit mantissa and scale. ---------- Also fixes `decimal → Half` and `decimal → BFloat16`, which is dependent on the same work, addressing that part of #112474. - These previously routed through `float` (`(Half)(float)value`), which double-rounds: `float`'s 24-bit significand only just meets the `2q + 2` bound needed for the second rounding to be innocuous (`q = 11` for `Half`, so `24 == 2*11 + 2` with no margin), so the result was not always correctly rounded. - Both now route through `double` (`(Half)(double)value`), whose 53-bit significand comfortably satisfies the bound for both types, making the conversion correctly rounded. `Half`/`BFloat16 → decimal` was already exact (both are exactly representable as `float`) and is unchanged. ## Performance Local BenchmarkDotNet (per 16-element batch, `InProcessEmitToolchain`), comparing the correctly-rounded implementation against the previous (incorrect) one: | Direction | Before fix | After fix | |---|---|---| | decimal→double | 28.4 ns | 64.0 ns | | decimal→float | 32.0 ns | 80.5 ns | | double→decimal | 127.8 ns | 177.8 ns | | float→decimal | 181.5 ns | 123.6 ns | The `decimal → floating-point` common case runs at the previous speed via the Clinger / Eisel-Lemire fast paths; the residual is entirely the wide-mantissa (>64-bit) inputs that require the exact division for correctness — the very inputs the old code rounded incorrectly. `double → decimal` is +39% (~+3 ns/op), a correctness-justified regression offset by `float → decimal` being ~32% faster. ## Testing Updated the affected expected-value test data and added round-trip / overflow regression tests, including new `decimal → Half`/`BFloat16` conversion coverage (correctly-rounded double-rounding cases, subnormals, and overflow). All pass, bit-identical to the correctly-rounded reference: - System.Runtime.Tests: 69797 / 0 - System.Runtime.Extensions.Tests: 8648 / 0 - System.Runtime.Numerics.Tests: 3103 / 0 Two existing tests encoded the old truncated `new decimal(double)` result and were updated to the correctly-rounded expansion (the same category of change #72135 anticipates): - `System.IO.Tests.StringWriterTests.TestWriteMisc` - `System.Data.Tests.SqlTypes.SqlDecimalTest.ReadWriteXmlTest` > [!NOTE] > This pull request was created by GitHub Copilot. --------- Co-authored-by: Copilot App <[email protected]>
1 parent 870aaff commit 8d67bbd

14 files changed

Lines changed: 521 additions & 354 deletions

File tree

‎src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlDecimalTest.cs‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -566,8 +566,10 @@ internal void ReadWriteXmlTestInternal(string xml,
566566
//[Category ("MobileNotWorking")]
567567
public void ReadWriteXmlTest()
568568
{
569-
string xml1 = "<?xml version=\"1.0\" encoding=\"utf-16\"?><decimal>4556.89756</decimal>";
570-
string xml2 = "<?xml version=\"1.0\" encoding=\"utf-16\"?><decimal>-6445.9999</decimal>";
569+
// These reflect the exact decimal representation of the double values below. Converting a
570+
// double to decimal is correctly rounded, so it preserves the full value rather than truncating.
571+
string xml1 = "<?xml version=\"1.0\" encoding=\"utf-16\"?><decimal>4556.8975600000003396417014301</decimal>";
572+
string xml2 = "<?xml version=\"1.0\" encoding=\"utf-16\"?><decimal>-6445.9998999999997977283783257</decimal>";
571573
string xml3 = "<?xml version=\"1.0\" encoding=\"utf-16\"?><decimal>0x455687AB3E4D56F</decimal>";
572574
decimal test1 = new decimal(4556.89756);
573575
// This one fails because of a possible conversion bug

‎src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs‎

Lines changed: 235 additions & 284 deletions
Large diffs are not rendered by default.

‎src/libraries/System.Private.CoreLib/src/System/Half.cs‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,10 @@ public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, [StringS
558558
/// <summary>Explicitly converts a <see cref="decimal" /> value to its nearest representable half-precision floating-point value.</summary>
559559
/// <param name="value">The value to convert.</param>
560560
/// <returns><paramref name="value" /> converted to its nearest representable half-precision floating-point value.</returns>
561-
public static explicit operator Half(decimal value) => (Half)(float)value;
561+
// Round through double, not float: decimal -> double is correctly rounded and double (53-bit significand)
562+
// -> Half is an innocuous double rounding (53 >= 2 * 11 + 2), so the result is correctly rounded. Going
563+
// through float (24-bit significand) is not, because 24 only meets the 2 * 11 + 2 bound with no margin.
564+
public static explicit operator Half(decimal value) => (Half)(double)value;
562565

563566
/// <summary>Explicitly converts a <see cref="double" /> value to its nearest representable half-precision floating-point value.</summary>
564567
/// <param name="value">The value to convert.</param>

‎src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace System
1111
{
1212
internal unsafe partial class Number
1313
{
14-
private static ReadOnlySpan<double> Pow10DoubleTable =>
14+
internal static ReadOnlySpan<double> Pow10DoubleTable =>
1515
[
1616
1e0, // 10^0
1717
1e1, // 10^1

‎src/libraries/System.Private.CoreLib/src/System/Numerics/BFloat16.cs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,9 @@ public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, [StringS
442442
/// <summary>Explicitly converts a <see cref="decimal" /> value to its nearest representable <see cref="BFloat16"/> value.</summary>
443443
/// <param name="value">The value to convert.</param>
444444
/// <returns><paramref name="value" /> converted to its nearest representable <see cref="BFloat16"/> value.</returns>
445-
public static explicit operator BFloat16(decimal value) => (BFloat16)(float)value;
445+
// Round through double, not float: decimal -> double is correctly rounded and double (53-bit significand)
446+
// -> BFloat16 is an innocuous double rounding (53 >= 2 * 8 + 2), so the result is correctly rounded.
447+
public static explicit operator BFloat16(decimal value) => (BFloat16)(double)value;
446448

447449
/// <summary>Explicitly converts a <see cref="double" /> value to its nearest representable <see cref="BFloat16"/> value.</summary>
448450
/// <param name="value">The value to convert.</param>

‎src/libraries/System.Runtime.Numerics/tests/ComplexTests.GenericMath.cs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,12 +2235,12 @@ public static void TryConvertToCheckedCharTest()
22352235
[Fact]
22362236
public static void TryConvertToCheckedDecimalTest()
22372237
{
2238-
Assert.Equal(-79228162514264300000000000000.0m, NumberBaseHelper<decimal>.CreateChecked<Complex>(-79228162514264333195497439231.0));
2238+
Assert.Equal(-79228162514264328797450928128.0m, NumberBaseHelper<decimal>.CreateChecked<Complex>(-79228162514264333195497439231.0));
22392239
Assert.Equal(-1.0m, NumberBaseHelper<decimal>.CreateChecked<Complex>(-1.0));
22402240
Assert.Equal(-0.0m, NumberBaseHelper<decimal>.CreateChecked<Complex>(-0.0));
22412241
Assert.Equal(+0.0m, NumberBaseHelper<decimal>.CreateChecked<Complex>(+0.0));
22422242
Assert.Equal(+1.0m, NumberBaseHelper<decimal>.CreateChecked<Complex>(+1.0));
2243-
Assert.Equal(+79228162514264300000000000000.0m, NumberBaseHelper<decimal>.CreateChecked<Complex>(+79228162514264333195497439231.0));
2243+
Assert.Equal(+79228162514264328797450928128.0m, NumberBaseHelper<decimal>.CreateChecked<Complex>(+79228162514264333195497439231.0));
22442244
}
22452245

22462246
[Fact]

‎src/libraries/System.Runtime/tests/System.IO.Tests/StringWriter/StringWriterTests.cs‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,12 @@ public static void TestWriteMisc()
325325
sw.Write((uint)uint.MaxValue);
326326
sw.Write((ulong)ulong.MaxValue);
327327

328-
Assert.Equal("Truea1234.013452342.0123456-92233720368547758081234.5429496729518446744073709551615", sw.ToString());
328+
// The expected string is the concatenation of every value above with no separators, so
329+
// adjacent values run together (e.g. "1234.00..." from the decimal is immediately followed
330+
// by "3452342.01" from the double). The decimal segment is the full value of new decimal(1234.01):
331+
// the double nearest 1234.01 is actually 1234.009999999999990905052982270717620849609375, and
332+
// double -> decimal is correctly rounded, so it preserves that value rather than truncating to "1234.01".
333+
Assert.Equal("Truea1234.00999999999999090505298233452342.0123456-92233720368547758081234.5429496729518446744073709551615", sw.ToString());
329334
}
330335
}
331336

‎src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Convert.ToDecimal.cs‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Globalization;
45
using Xunit;
56

67
namespace System.Tests
@@ -34,14 +35,19 @@ public void FromDecimal()
3435
[Fact]
3536
public void FromDouble()
3637
{
38+
// 0.001 is inexact as a double, so the expected value is derived from parsing its full base-10
39+
// expansion to exercise the correctly-rounded (double -> decimal) conversion.
3740
double[] testValues = { 1000.0, 100.0, 0.0, 0.001, -1000.0, -100.0, };
38-
decimal[] expectedValues = { 1000.0m, 100.0m, 0.0m, 0.001m, -1000.0m, -100.0m };
41+
decimal[] expectedValues = { 1000.0m, 100.0m, 0.0m, ParseDecimalExpansion(0.001), -1000.0m, -100.0m };
3942
Verify(Convert.ToDecimal, testValues, expectedValues);
4043

4144
double[] overflowValues = { double.MaxValue, double.MinValue };
4245
VerifyThrows<OverflowException, double>(Convert.ToDecimal, overflowValues);
4346
}
4447

48+
private static decimal ParseDecimalExpansion(double value) =>
49+
decimal.Parse(value.ToString("G99", CultureInfo.InvariantCulture), NumberStyles.Float, CultureInfo.InvariantCulture);
50+
4551
[Fact]
4652
public void FromInt16()
4753
{

‎src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Math.cs‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2477,6 +2477,32 @@ public static void Round_Double_Digits_ByMidpointRounding(MidpointRounding mode)
24772477
Assert.Equal(double.NegativeInfinity, Math.Round(double.NegativeInfinity, 3, mode));
24782478
}
24792479

2480+
public static IEnumerable<object[]> Round_Digits_Decimal_TestData
2481+
{
2482+
get
2483+
{
2484+
yield return new object[] {0m, 0m, 3, MidpointRounding.ToEven};
2485+
yield return new object[] {3.42156m, 3.422m, 3, MidpointRounding.ToEven};
2486+
yield return new object[] {-3.42156m, -3.422m, 3, MidpointRounding.ToEven};
2487+
2488+
yield return new object[] {0m, 0m, 3, MidpointRounding.AwayFromZero};
2489+
yield return new object[] {3.42156m, 3.422m, 3, MidpointRounding.AwayFromZero};
2490+
yield return new object[] {-3.42156m, -3.422m, 3, MidpointRounding.AwayFromZero};
2491+
2492+
yield return new object[] {0m, 0m, 3, MidpointRounding.ToZero};
2493+
yield return new object[] {3.42156m, 3.421m, 3, MidpointRounding.ToZero};
2494+
yield return new object[] {-3.42156m, -3.421m, 3, MidpointRounding.ToZero};
2495+
2496+
yield return new object[] {0m, 0m, 3, MidpointRounding.ToNegativeInfinity};
2497+
yield return new object[] {3.42156m, 3.421m, 3, MidpointRounding.ToNegativeInfinity};
2498+
yield return new object[] {-3.42156m, -3.422m, 3, MidpointRounding.ToNegativeInfinity};
2499+
2500+
yield return new object[] {0m, 0m, 3, MidpointRounding.ToPositiveInfinity};
2501+
yield return new object[] {3.42156m, 3.422m, 3, MidpointRounding.ToPositiveInfinity};
2502+
yield return new object[] {-3.42156m, -3.421m, 3, MidpointRounding.ToPositiveInfinity};
2503+
}
2504+
}
2505+
24802506
[Theory]
24812507
[MemberData(nameof(Round_Digits_TestData))]
24822508
public static void Round_Double_Digits(double x, double expected, int digits, MidpointRounding mode)
@@ -2485,7 +2511,7 @@ public static void Round_Double_Digits(double x, double expected, int digits, Mi
24852511
}
24862512

24872513
[Theory]
2488-
[MemberData(nameof(Round_Digits_TestData))]
2514+
[MemberData(nameof(Round_Digits_Decimal_TestData))]
24892515
public static void Round_Decimal_Digits(decimal x, decimal expected, int digits, MidpointRounding mode)
24902516
{
24912517
Assert.Equal(expected, Math.Round(x, digits, mode));

‎src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalTests.GenericMath.cs‎

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -608,8 +608,8 @@ public static void CreateCheckedFromDoubleTest()
608608
Assert.Equal(+1.0m, NumberBaseHelper<decimal>.CreateChecked<double>(+1.0));
609609
Assert.Equal(-1.0m, NumberBaseHelper<decimal>.CreateChecked<double>(-1.0));
610610

611-
Assert.Equal(+79228162514264300000000000000.0m, NumberBaseHelper<decimal>.CreateChecked<double>(+79228162514264333195497439231.0));
612-
Assert.Equal(-79228162514264300000000000000.0m, NumberBaseHelper<decimal>.CreateChecked<double>(-79228162514264333195497439231.0));
611+
Assert.Equal(+79228162514264328797450928128.0m, NumberBaseHelper<decimal>.CreateChecked<double>(+79228162514264333195497439231.0));
612+
Assert.Equal(-79228162514264328797450928128.0m, NumberBaseHelper<decimal>.CreateChecked<double>(-79228162514264333195497439231.0));
613613

614614
Assert.Throws<OverflowException>(() => NumberBaseHelper<decimal>.CreateChecked<double>(+79228162514264337593543950335.0));
615615
Assert.Throws<OverflowException>(() => NumberBaseHelper<decimal>.CreateChecked<double>(-79228162514264337593543950335.0));
@@ -629,8 +629,8 @@ public static void CreateCheckedFromHalfTest()
629629
Assert.Equal(0.0m, NumberBaseHelper<decimal>.CreateChecked<Half>(Half.Zero));
630630
Assert.Equal(0.0m, NumberBaseHelper<decimal>.CreateChecked<Half>(Half.NegativeZero));
631631

632-
Assert.Equal(+0.00000005960464m, NumberBaseHelper<decimal>.CreateChecked<Half>(+Half.Epsilon));
633-
Assert.Equal(-0.00000005960464m, NumberBaseHelper<decimal>.CreateChecked<Half>(-Half.Epsilon));
632+
Assert.Equal(+0.000000059604644775390625m, NumberBaseHelper<decimal>.CreateChecked<Half>(+Half.Epsilon));
633+
Assert.Equal(-0.000000059604644775390625m, NumberBaseHelper<decimal>.CreateChecked<Half>(-Half.Epsilon));
634634

635635
Assert.Equal(+1.0m, NumberBaseHelper<decimal>.CreateChecked<Half>(Half.One));
636636
Assert.Equal(-1.0m, NumberBaseHelper<decimal>.CreateChecked<Half>(Half.NegativeOne));
@@ -749,8 +749,8 @@ public static void CreateCheckedFromSingleTest()
749749
Assert.Equal(+1.0m, NumberBaseHelper<decimal>.CreateChecked<float>(+1.0f));
750750
Assert.Equal(-1.0m, NumberBaseHelper<decimal>.CreateChecked<float>(-1.0f));
751751

752-
Assert.Equal(+79228160000000000000000000000.0m, NumberBaseHelper<decimal>.CreateChecked<float>(+79228160000000000000000000000.0f));
753-
Assert.Equal(-79228160000000000000000000000.0m, NumberBaseHelper<decimal>.CreateChecked<float>(-79228160000000000000000000000.0f));
752+
Assert.Equal(+79228157791897854723898736640.0m, NumberBaseHelper<decimal>.CreateChecked<float>(+79228160000000000000000000000.0f));
753+
Assert.Equal(-79228157791897854723898736640.0m, NumberBaseHelper<decimal>.CreateChecked<float>(-79228160000000000000000000000.0f));
754754

755755
Assert.Throws<OverflowException>(() => NumberBaseHelper<decimal>.CreateChecked<float>(+79228162514264337593543950335.0f));
756756
Assert.Throws<OverflowException>(() => NumberBaseHelper<decimal>.CreateChecked<float>(-79228162514264337593543950335.0f));
@@ -869,8 +869,8 @@ public static void CreateSaturatingFromDoubleTest()
869869
Assert.Equal(+1.0m, NumberBaseHelper<decimal>.CreateSaturating<double>(+1.0));
870870
Assert.Equal(-1.0m, NumberBaseHelper<decimal>.CreateSaturating<double>(-1.0));
871871

872-
Assert.Equal(+79228162514264300000000000000.0m, NumberBaseHelper<decimal>.CreateSaturating<double>(+79228162514264333195497439231.0));
873-
Assert.Equal(-79228162514264300000000000000.0m, NumberBaseHelper<decimal>.CreateSaturating<double>(-79228162514264333195497439231.0));
872+
Assert.Equal(+79228162514264328797450928128.0m, NumberBaseHelper<decimal>.CreateSaturating<double>(+79228162514264333195497439231.0));
873+
Assert.Equal(-79228162514264328797450928128.0m, NumberBaseHelper<decimal>.CreateSaturating<double>(-79228162514264333195497439231.0));
874874

875875
Assert.Equal(decimal.MaxValue, NumberBaseHelper<decimal>.CreateSaturating<double>(+79228162514264337593543950335.0));
876876
Assert.Equal(decimal.MinValue, NumberBaseHelper<decimal>.CreateSaturating<double>(-79228162514264337593543950335.0));
@@ -890,8 +890,8 @@ public static void CreateSaturatingFromHalfTest()
890890
Assert.Equal(0.0m, NumberBaseHelper<decimal>.CreateSaturating<Half>(Half.Zero));
891891
Assert.Equal(0.0m, NumberBaseHelper<decimal>.CreateSaturating<Half>(Half.NegativeZero));
892892

893-
Assert.Equal(+0.00000005960464m, NumberBaseHelper<decimal>.CreateSaturating<Half>(+Half.Epsilon));
894-
Assert.Equal(-0.00000005960464m, NumberBaseHelper<decimal>.CreateSaturating<Half>(-Half.Epsilon));
893+
Assert.Equal(+0.000000059604644775390625m, NumberBaseHelper<decimal>.CreateSaturating<Half>(+Half.Epsilon));
894+
Assert.Equal(-0.000000059604644775390625m, NumberBaseHelper<decimal>.CreateSaturating<Half>(-Half.Epsilon));
895895

896896
Assert.Equal(+1.0m, NumberBaseHelper<decimal>.CreateSaturating<Half>(Half.One));
897897
Assert.Equal(-1.0m, NumberBaseHelper<decimal>.CreateSaturating<Half>(Half.NegativeOne));
@@ -1011,8 +1011,8 @@ public static void CreateSaturatingFromSingleTest()
10111011
Assert.Equal(+1.0m, NumberBaseHelper<decimal>.CreateSaturating<float>(+1.0f));
10121012
Assert.Equal(-1.0m, NumberBaseHelper<decimal>.CreateSaturating<float>(-1.0f));
10131013

1014-
Assert.Equal(+79228160000000000000000000000.0m, NumberBaseHelper<decimal>.CreateSaturating<float>(+79228160000000000000000000000.0f));
1015-
Assert.Equal(-79228160000000000000000000000.0m, NumberBaseHelper<decimal>.CreateSaturating<float>(-79228160000000000000000000000.0f));
1014+
Assert.Equal(+79228157791897854723898736640.0m, NumberBaseHelper<decimal>.CreateSaturating<float>(+79228160000000000000000000000.0f));
1015+
Assert.Equal(-79228157791897854723898736640.0m, NumberBaseHelper<decimal>.CreateSaturating<float>(-79228160000000000000000000000.0f));
10161016

10171017
Assert.Equal(decimal.MaxValue, NumberBaseHelper<decimal>.CreateSaturating<float>(+79228162514264337593543950335.0f));
10181018
Assert.Equal(decimal.MinValue, NumberBaseHelper<decimal>.CreateSaturating<float>(-79228162514264337593543950335.0f));
@@ -1132,8 +1132,8 @@ public static void CreateTruncatingFromDoubleTest()
11321132
Assert.Equal(+1.0m, NumberBaseHelper<decimal>.CreateTruncating<double>(+1.0));
11331133
Assert.Equal(-1.0m, NumberBaseHelper<decimal>.CreateTruncating<double>(-1.0));
11341134

1135-
Assert.Equal(+79228162514264300000000000000.0m, NumberBaseHelper<decimal>.CreateTruncating<double>(+79228162514264333195497439231.0));
1136-
Assert.Equal(-79228162514264300000000000000.0m, NumberBaseHelper<decimal>.CreateTruncating<double>(-79228162514264333195497439231.0));
1135+
Assert.Equal(+79228162514264328797450928128.0m, NumberBaseHelper<decimal>.CreateTruncating<double>(+79228162514264333195497439231.0));
1136+
Assert.Equal(-79228162514264328797450928128.0m, NumberBaseHelper<decimal>.CreateTruncating<double>(-79228162514264333195497439231.0));
11371137

11381138
Assert.Equal(decimal.MaxValue, NumberBaseHelper<decimal>.CreateTruncating<double>(+79228162514264337593543950335.0));
11391139
Assert.Equal(decimal.MinValue, NumberBaseHelper<decimal>.CreateTruncating<double>(-79228162514264337593543950335.0));
@@ -1153,8 +1153,8 @@ public static void CreateTruncatingFromHalfTest()
11531153
Assert.Equal(0.0m, NumberBaseHelper<decimal>.CreateTruncating<Half>(Half.Zero));
11541154
Assert.Equal(0.0m, NumberBaseHelper<decimal>.CreateTruncating<Half>(Half.NegativeZero));
11551155

1156-
Assert.Equal(+0.00000005960464m, NumberBaseHelper<decimal>.CreateTruncating<Half>(+Half.Epsilon));
1157-
Assert.Equal(-0.00000005960464m, NumberBaseHelper<decimal>.CreateTruncating<Half>(-Half.Epsilon));
1156+
Assert.Equal(+0.000000059604644775390625m, NumberBaseHelper<decimal>.CreateTruncating<Half>(+Half.Epsilon));
1157+
Assert.Equal(-0.000000059604644775390625m, NumberBaseHelper<decimal>.CreateTruncating<Half>(-Half.Epsilon));
11581158

11591159
Assert.Equal(+1.0m, NumberBaseHelper<decimal>.CreateTruncating<Half>(Half.One));
11601160
Assert.Equal(-1.0m, NumberBaseHelper<decimal>.CreateTruncating<Half>(Half.NegativeOne));
@@ -1274,8 +1274,8 @@ public static void CreateTruncatingFromSingleTest()
12741274
Assert.Equal(+1.0m, NumberBaseHelper<decimal>.CreateTruncating<float>(+1.0f));
12751275
Assert.Equal(-1.0m, NumberBaseHelper<decimal>.CreateTruncating<float>(-1.0f));
12761276

1277-
Assert.Equal(+79228160000000000000000000000.0m, NumberBaseHelper<decimal>.CreateTruncating<float>(+79228160000000000000000000000.0f));
1278-
Assert.Equal(-79228160000000000000000000000.0m, NumberBaseHelper<decimal>.CreateTruncating<float>(-79228160000000000000000000000.0f));
1277+
Assert.Equal(+79228157791897854723898736640.0m, NumberBaseHelper<decimal>.CreateTruncating<float>(+79228160000000000000000000000.0f));
1278+
Assert.Equal(-79228157791897854723898736640.0m, NumberBaseHelper<decimal>.CreateTruncating<float>(-79228160000000000000000000000.0f));
12791279

12801280
Assert.Equal(decimal.MaxValue, NumberBaseHelper<decimal>.CreateTruncating<float>(+79228162514264337593543950335.0f));
12811281
Assert.Equal(decimal.MinValue, NumberBaseHelper<decimal>.CreateTruncating<float>(-79228162514264337593543950335.0f));

0 commit comments

Comments
 (0)