Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.

Commit 4e38dd8

Browse files
hamish-rosejkotas
authored andcommitted
Add new rounding modes to System.Math, System.MathF (dotnet/coreclr#20815)
* add new rounding modes to MidpointRounding.cs new modes added to enum implemented ToZero for double in Math.cs * ToZero implementation * implement double and float rounding modes * updating rounding implementation now round inline with DecCalc internal round implementation * small bug fix also replace var to make things obvious * update implementation - floor/ceil code review feedback * review feedback add comments, update MathF with floor/ceil * code review feedback - fix comments - replace ifelse with switch - remove RoundingMode enum from DecCalc * exclude outdated corefx test Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
1 parent 36d23ac commit 4e38dd8

File tree

5 files changed

+102
-46
lines changed

5 files changed

+102
-46
lines changed

src/System.Private.CoreLib/shared/System/Decimal.DecCalc.cs

+9-20
Original file line numberDiff line numberDiff line change
@@ -1243,7 +1243,7 @@ internal static long VarCyFromDec(ref DecCalc pdecIn)
12431243
else
12441244
{
12451245
if (scale != 0)
1246-
InternalRound(ref pdecIn, (uint)scale, RoundingMode.ToEven);
1246+
InternalRound(ref pdecIn, (uint)scale, MidpointRounding.ToEven);
12471247
if (pdecIn.High != 0)
12481248
goto ThrowOverflow;
12491249
value = (long)pdecIn.Low64;
@@ -2408,19 +2408,8 @@ private static unsafe void VarDecModFull(ref DecCalc d1, ref DecCalc d2, int sca
24082408
}
24092409
}
24102410

2411-
internal enum RoundingMode
2412-
{
2413-
ToEven = 0,
2414-
AwayFromZero = 1,
2415-
Truncate = 2,
2416-
Floor = 3,
2417-
Ceiling = 4,
2418-
}
2419-
2420-
/// <summary>
2421-
/// Does an in-place round by the specified scale
2422-
/// </summary>
2423-
internal static void InternalRound(ref DecCalc d, uint scale, RoundingMode mode)
2411+
// Does an in-place round by the specified scale
2412+
internal static void InternalRound(ref DecCalc d, uint scale, MidpointRounding mode)
24242413
{
24252414
// the scale becomes the desired decimal count
24262415
d.uflags -= scale << ScaleShift;
@@ -2473,7 +2462,7 @@ internal static void InternalRound(ref DecCalc d, uint scale, RoundingMode mode)
24732462
ulong tmp = d.Low64;
24742463
if (tmp == 0)
24752464
{
2476-
if (mode <= RoundingMode.Truncate)
2465+
if (mode <= MidpointRounding.ToZero)
24772466
goto done;
24782467
remainder = 0;
24792468
goto checkRemainder;
@@ -2503,9 +2492,9 @@ internal static void InternalRound(ref DecCalc d, uint scale, RoundingMode mode)
25032492
}
25042493

25052494
checkRemainder:
2506-
if (mode == RoundingMode.Truncate)
2495+
if (mode == MidpointRounding.ToZero)
25072496
goto done;
2508-
else if (mode == RoundingMode.ToEven)
2497+
else if (mode == MidpointRounding.ToEven)
25092498
{
25102499
// To do IEEE rounding, we add LSB of result to sticky bits so either causes round up if remainder * 2 == last divisor.
25112500
remainder <<= 1;
@@ -2514,22 +2503,22 @@ internal static void InternalRound(ref DecCalc d, uint scale, RoundingMode mode)
25142503
if (power >= remainder)
25152504
goto done;
25162505
}
2517-
else if (mode == RoundingMode.AwayFromZero)
2506+
else if (mode == MidpointRounding.AwayFromZero)
25182507
{
25192508
// Round away from zero at the mid point.
25202509
remainder <<= 1;
25212510
if (power > remainder)
25222511
goto done;
25232512
}
2524-
else if (mode == RoundingMode.Floor)
2513+
else if (mode == MidpointRounding.ToNegativeInfinity)
25252514
{
25262515
// Round toward -infinity if we have chopped off a non-zero amount from a negative value.
25272516
if ((remainder | sticky) == 0 || !d.IsNegative)
25282517
goto done;
25292518
}
25302519
else
25312520
{
2532-
Debug.Assert(mode == RoundingMode.Ceiling);
2521+
Debug.Assert(mode == MidpointRounding.ToPositiveInfinity);
25332522
// Round toward infinity if we have chopped off a non-zero amount from a positive value.
25342523
if ((remainder | sticky) == 0 || d.IsNegative)
25352524
goto done;

src/System.Private.CoreLib/shared/System/Decimal.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ public static decimal Ceiling(decimal d)
327327
{
328328
int flags = d.flags;
329329
if ((flags & ScaleMask) != 0)
330-
DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), DecCalc.RoundingMode.Ceiling);
330+
DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), MidpointRounding.ToPositiveInfinity);
331331
return d;
332332
}
333333

@@ -407,7 +407,7 @@ public static decimal Floor(decimal d)
407407
{
408408
int flags = d.flags;
409409
if ((flags & ScaleMask) != 0)
410-
DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), DecCalc.RoundingMode.Floor);
410+
DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), MidpointRounding.ToNegativeInfinity);
411411
return d;
412412
}
413413

@@ -617,12 +617,12 @@ private static decimal Round(ref decimal d, int decimals, MidpointRounding mode)
617617
{
618618
if ((uint)decimals > 28)
619619
throw new ArgumentOutOfRangeException(nameof(decimals), SR.ArgumentOutOfRange_DecimalRound);
620-
if ((uint)mode > (uint)MidpointRounding.AwayFromZero)
620+
if ((uint)mode > (uint)MidpointRounding.ToPositiveInfinity)
621621
throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode));
622622

623623
int scale = d.Scale - decimals;
624624
if (scale > 0)
625-
DecCalc.InternalRound(ref AsMutable(ref d), (uint)scale, (DecCalc.RoundingMode)mode);
625+
DecCalc.InternalRound(ref AsMutable(ref d), (uint)scale, mode);
626626
return d;
627627
}
628628

@@ -829,7 +829,7 @@ private static void Truncate(ref decimal d)
829829
{
830830
int flags = d.flags;
831831
if ((flags & ScaleMask) != 0)
832-
DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), DecCalc.RoundingMode.Truncate);
832+
DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), MidpointRounding.ToZero);
833833
}
834834

835835
public static implicit operator decimal(byte value)

src/System.Private.CoreLib/shared/System/Math.cs

+42-10
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,7 @@ public static unsafe double Round(double value, int digits, MidpointRounding mod
892892
throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits);
893893
}
894894

895-
if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero)
895+
if (mode < MidpointRounding.ToEven || mode > MidpointRounding.ToPositiveInfinity)
896896
{
897897
throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode));
898898
}
@@ -903,20 +903,52 @@ public static unsafe double Round(double value, int digits, MidpointRounding mod
903903

904904
value *= power10;
905905

906-
if (mode == MidpointRounding.AwayFromZero)
906+
switch (mode)
907907
{
908-
var fraction = ModF(value, &value);
908+
// Rounds to the nearest value; if the number falls midway,
909+
// it is rounded to the nearest value with an even least significant digit
910+
case MidpointRounding.ToEven:
911+
{
912+
value = Round(value);
913+
break;
914+
}
915+
// Rounds to the nearest value; if the number falls midway,
916+
// it is rounded to the nearest value above (for positive numbers) or below (for negative numbers)
917+
case MidpointRounding.AwayFromZero:
918+
{
919+
double fraction = ModF(value, &value);
920+
921+
if (Abs(fraction) >= 0.5)
922+
{
923+
value += Sign(fraction);
924+
}
909925

910-
if (Abs(fraction) >= 0.5)
926+
break;
927+
}
928+
// Directed rounding: Round to the nearest value, toward to zero
929+
case MidpointRounding.ToZero:
911930
{
912-
value += Sign(fraction);
931+
value = Truncate(value);
932+
break;
933+
}
934+
// Directed Rounding: Round down to the next value, toward negative infinity
935+
case MidpointRounding.ToNegativeInfinity:
936+
{
937+
value = Floor(value);
938+
break;
939+
}
940+
// Directed rounding: Round up to the next value, toward positive infinity
941+
case MidpointRounding.ToPositiveInfinity:
942+
{
943+
value = Ceiling(value);
944+
break;
945+
}
946+
default:
947+
{
948+
throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode));
913949
}
914950
}
915-
else
916-
{
917-
value = Round(value);
918-
}
919-
951+
920952
value /= power10;
921953
}
922954

src/System.Private.CoreLib/shared/System/MathF.cs

+43-11
Original file line numberDiff line numberDiff line change
@@ -306,9 +306,9 @@ public static unsafe float Round(float x, int digits, MidpointRounding mode)
306306
throw new ArgumentOutOfRangeException(nameof(digits), SR.ArgumentOutOfRange_RoundingDigits);
307307
}
308308

309-
if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero)
309+
if (mode < MidpointRounding.ToEven || mode > MidpointRounding.ToPositiveInfinity)
310310
{
311-
throw new ArgumentException(SR.Format(SR.Argument_InvalidEnum, mode, nameof(MidpointRounding)), nameof(mode));
311+
throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode));
312312
}
313313

314314
if (Abs(x) < singleRoundLimit)
@@ -317,20 +317,52 @@ public static unsafe float Round(float x, int digits, MidpointRounding mode)
317317

318318
x *= power10;
319319

320-
if (mode == MidpointRounding.AwayFromZero)
320+
switch (mode)
321321
{
322-
var fraction = ModF(x, &x);
322+
// Rounds to the nearest value; if the number falls midway,
323+
// it is rounded to the nearest value with an even least significant digit
324+
case MidpointRounding.ToEven:
325+
{
326+
x = Round(x);
327+
break;
328+
}
329+
// Rounds to the nearest value; if the number falls midway,
330+
// it is rounded to the nearest value above (for positive numbers) or below (for negative numbers)
331+
case MidpointRounding.AwayFromZero:
332+
{
333+
float fraction = ModF(x, &x);
334+
335+
if (Abs(fraction) >= 0.5)
336+
{
337+
x += Sign(fraction);
338+
}
323339

324-
if (Abs(fraction) >= 0.5f)
340+
break;
341+
}
342+
// Directed rounding: Round to the nearest value, toward to zero
343+
case MidpointRounding.ToZero:
325344
{
326-
x += Sign(fraction);
345+
x = Truncate(x);
346+
break;
347+
}
348+
// Directed Rounding: Round down to the next value, toward negative infinity
349+
case MidpointRounding.ToNegativeInfinity:
350+
{
351+
x = Floor(x);
352+
break;
353+
}
354+
// Directed rounding: Round up to the next value, toward positive infinity
355+
case MidpointRounding.ToPositiveInfinity:
356+
{
357+
x = Ceiling(x);
358+
break;
359+
}
360+
default:
361+
{
362+
throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode));
327363
}
328364
}
329-
else
330-
{
331-
x = Round(x);
332-
}
333-
365+
334366
x /= power10;
335367
}
336368

src/System.Private.CoreLib/shared/System/MidpointRounding.cs

+3
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@ public enum MidpointRounding
88
{
99
ToEven = 0,
1010
AwayFromZero = 1,
11+
ToZero = 2,
12+
ToNegativeInfinity = 3,
13+
ToPositiveInfinity = 4
1114
}
1215
}

0 commit comments

Comments
 (0)