-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Add new rounding modes to System.Math, System.MathF #20815
Add new rounding modes to System.Math, System.MathF #20815
Conversation
{ | ||
double fraction = ModF(value, &value); | ||
|
||
if (Sign(fraction) == -1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should only come into play for ties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Decimal.DecCalc.InternalRound with Truncate mode supplied (AKA ToZero if i'm not mistaken) currently behaves like this aswell.
e.g. these scenarios all pass..
[InlineData(11.0, 11, MidpointRounding.ToZero)]
[InlineData(11.4, 11, MidpointRounding.ToZero)]
[InlineData(11.5, 11, MidpointRounding.ToZero)]
[InlineData(11.6, 11, MidpointRounding.ToZero)]
[InlineData(-11.0, -11, MidpointRounding.ToZero)]
[InlineData(-11.4, -11, MidpointRounding.ToZero)]
[InlineData(-11.5, -11, MidpointRounding.ToZero)]
[InlineData(-11.6, -11, MidpointRounding.ToZero)]
public static void Round_Decimal_Modes(decimal x, decimal expected, MidpointRounding mode)
{
Assert.Equal(expected, Math.Round(x, 0, mode));
Assert.Equal(expected, decimal.Round(x, 0, mode));
}
Agreed its not really midpoint rounding though, perhaps Math.Round(decimal, ..) could be implemented in Math.cs ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Sorry, I was thinking about the case where you are parsing a result.
Having comments explaining each case would be helpful. We should also just be able to use Ceiling
, Floor
, and Truncate
(for ToPositiveInfinity
, ToNegativeInfinity
, and Truncate
, respectively)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Sorry, I was thinking about the case where you are parsing a result.
Having comments explaining each case would be helpful. We should also just be able to use
Ceiling
,Floor
, andTruncate
(forToPositiveInfinity
,ToNegativeInfinity
, andTruncate
, respectively)
Done, added some comments and now using Floor, Ceiling and Truncate.
value--; | ||
} | ||
} | ||
else if (mode is MidpointRounding.ToPositiveInfinity) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The result shall be the format’s floating-point number (possibly +∞) closest to and no less than the infinitely precise result.
value = Round(value); | ||
value = Truncate(value); | ||
} | ||
else if (mode is MidpointRounding.ToNegativeInfinity) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The result shall be the format’s floating-point number (possibly −∞) closest to and no greater than the infinitely precise result
|
||
if (Abs(fraction) >= 0.5) | ||
{ | ||
value += Sign(fraction); | ||
} | ||
} | ||
else | ||
else if (mode is MidpointRounding.ToZero) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The result shall be the format’s floating-point number closest to and no greater in magnitude than the infinitely precise result.
{ | ||
var fraction = ModF(value, &value); | ||
value = Round(value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The floating-point number nearest to the infinitely precise result shall be delivered; if the two nearest floating-point numbers bracketing an unrepresentable infinitely precise result are equally near, the one with an even least significant digit shall be delivered
} | ||
else if (mode is MidpointRounding.AwayFromZero) | ||
{ | ||
double fraction = ModF(value, &value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The floating-point number nearest to the infinitely precise result shall be delivered; if the two nearest floating-point numbers bracketing an unrepresentable infinitely precise result are equally near, the one with larger magnitude shall be delivered.
@@ -183,18 +183,37 @@ public static unsafe float Round(float x, int digits, MidpointRounding mode) | |||
|
|||
x *= power10; | |||
|
|||
if (mode == MidpointRounding.AwayFromZero) | |||
if (mode is MidpointRounding.ToEven) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would a switch statement be clearer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so. I opted for if else here to be consistent with existing code such as Decimal.DecCalc.InternalRound.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated the implementation in Math and MathF with a switch statement
@hamish-rose I see you have tests in your corefx fork. What we typically do is put up the corefx PR with the tests at the same time as the coreclr PR with the implementation. Of course the build or tests fail in the corefx PR initially but it means that we can review the tests concurrently and we know before we merge the implementation that satisfcatory tests on it are ready. |
@danmosemsft i've raised dotnet/corefx#33297 |
test this please |
{ | ||
value = Round(value); | ||
// Directed rounding: Round toward zero, to nearest value above (positive numbers) or below (negative numbers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the comment needs fixing, we won't be rounding above for positive numbers, but rather the nearest exact or the nearest just below (closer to zero)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(sorry for the delay, this fell off my radar)
This looks correct to me (at least according to the current algorithm Math.Round(int digits)
is doing).
CC. @ViktorHofer, @eerhardt as it would be useful to have a second pair of eyes.
Would it make sense to get rid of
Refers to: src/System.Private.CoreLib/shared/System/Decimal.cs:625 in 072f5b8. [](commit_id = 072f5b8, deletion_comment = False) |
} | ||
else if (mode is MidpointRounding.ToNegativeInfinity) | ||
{ | ||
// Directed Rounding: Round down to the next value, toward −∞ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we typically put non-standard symbols in source code, as some editors/viewers don't show these correctly. Can we just put negative infinity
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the good work, @hamish-rose! Looks good.
@hamish-rose, sorry for the delay, this slipped off my radar. Could you please either merge or rebase this PR against the current head of master? Once that is done (and the jobs all pass), we should be good to merge. |
new modes added to enum implemented ToZero for double in Math.cs
now round inline with DecCalc internal round implementation
also replace var to make things obvious
code review feedback
add comments, update MathF with floor/ceil
- fix comments - replace ifelse with switch - remove RoundingMode enum from DecCalc
@tannergooding I believe I've rebased onto master, working on the failing test now. |
How did you rebase? I wouldn't expect quite so many commits. Many of them look like you may have rebased a merge commit... |
you can fix/restore most of the stuff with git reflog assuming you didn't remove local copy of the repository (copy the whole coreclr folder before just in case) |
On my local tracking branch I rebased it onto master, then when I pushed to my remote it 'merged' and now it a bit of a mess. Sorry about that, I'll give reflog a try. |
@tannergooding i've fixed the rebase now. The test that is failing is a corefx inner loop test that is obsolete now since we just have one enum for rounding mode. I think it can be removed once these changes are merged and mirrored in corefx |
For the failing CoreFX tests, we need to add an entry in the |
Thanks for the work here @hamish-rose |
…0815) * 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>
…0815) * 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>
…0815) * 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>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless I'm completely misunderstanding something, then this change incorrectly conflates midpoint rounding modes with truncation/ceiling/floor which apply to any rounded value, not just to midpoint!
OK, the IEEE 754 spec actually talks about directed rounding (truncate/floor/ceiling), but then this API naming is definitely wrong - it's not midpoint rounding. |
…0815) * 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>
@pentp, I've commented about your concerns on the original API proposal: https://github.com/dotnet/corefx/issues/31902. We should continue the discussion there, to help keep it in one location. |
…0815) * 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>
…0815) * 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>
#33297) * add tests for new midpointrounding modes new tests added for Math.cs double Round methods * add tests for float, decimal convert to theory * Update tests tests now reflect correct rounding behavior * add tests for existing modes * code review feedback - refactor tests to use member data - move MathF tests to MathF.netcoreapp.cs - add tests for new modes with digits and special cases * move math tests to mathtests.netcoreapp.cs netcoreapp specific tests moved
…0815) * 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 Commit migrated from dotnet/coreclr@3397472
resolves dotnet/corefx#31902
Adding new rounding modes to MidpointRounding for use with Math.Round and MathF.Round.
My unit tests are over on corefx at the moment. If I understand correctly I need my changes to be mirrored there by the bot and corefx be built with my coreclr version to run tests.
See https://github.com/hamish-rose/corefx/blob/MidpointRoundingmodes%2331902/src/System.Runtime.Extensions/tests/System/Math.cs#L1469