Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

BigInteger based random testing of System.Decimal #24053

Merged
merged 4 commits into from
Sep 25, 2017

Conversation

pentp
Copy link

@pentp pentp commented Sep 14, 2017

New comprehensive tests are needed for a significantly improved managed decimal implementation: dotnet/corert#4452 (comment), at first in CoreRT, but hopefully later shared with CoreCLR.

These tests compare System.Decimal cmp/add/mul/div results against a BigInteger based reference implementation using ~1000 different random decimals covering every scale and sign with ~20 different bit-patterns each (about 950k combinations). It adds about 1-2 seconds to System.Runtime test execution time (from 10s to 12s) - is this acceptable?

@danmoseley
Copy link
Member

Copy link
Contributor

@mellinoe mellinoe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overall approach , but I left a few comments and some questions. I'm especially concerned about the BigDecimal structure here -- it seems like a lot of the test validation is tied up in that implementation, which I'm not sure is correct.

[Fact]
public static void RandomBigInteger_Compare()
{
var values = GetRandomData(out var bigValues);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: don't use var or out var here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also may help to name these decimalValues and bigIntValues.

var values = GetRandomData(out var bigValues);
for (int i = 0, b = 0; i < values.Length; i++)
{
var d1 = values[i];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not see var throughout here. Everything has a similar name but lots of things have different types, which is important to highight.

bigDecimals = Array.ConvertAll(data, d => new BigDecimal(d));
return data;

int IncBitLimits(int i)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can there be a description for these values? They seem arbitrary.

}
}

struct BigDecimal
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a very large amount of non-trivial code in this structure. Could you explain a bit more about where it came from / why it is correct? Is this logic copy-pasted from BigInteger? Without understanding how this stuff is valid / trustworthy, it's hard to put a lot of value in the tests that rely on it.

public static void RandomBigInteger_Compare()
{
var values = GetRandomData(out var bigValues);
for (int i = 0, b = 0; i < values.Length; i++)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this b variable is actually used (it's only incremented).

var expected = b1.Add(bigValues[j], out var expectedOF);
if (expectedOF)
{
if (--overflowBudget < 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this for?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for limiting the number of overflow cases verified - without the limit there would be about 100k additional exceptions thrown (4-5s additional test execution time).

{
var overflowBudget = 1000;
var values = GetRandomData(out var bigValues);
for (int i = 0, b = 0; i < values.Length; i++)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

b is unused

{
var overflowBudget = 1000;
var values = GetRandomData(out var bigValues);
for (int i = 0, b = 0; i < values.Length; i++)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

b is unused.

@pentp
Copy link
Author

pentp commented Sep 16, 2017

The BigDecimal implementation is roughly based on the oleaut32 native decimal code currently used by CoreCLR/CoreRT (especially ScaleResult), but optimized for simplicity instead of speed. I could remove a few shortcuts from the code, but I think it can't be made much simpler.

I added more comments for the tests and BigDecimal code. The correctness of BigDecimal is ensured by the very same tests because they are symmetrical – the results should be always the same for System.Decimal and BigDecimal, except maybe if in the future we decide to remove the compatibility quirks of VarDecMul.

@mellinoe
Copy link
Contributor

The correctness of BigDecimal is ensured by the very same tests because they are symmetrical – the results should be always the same for System.Decimal and BigDecimal

Well, it ensures that they have the same behavior, but not that they are correct. They could both have the same bug, and the tests would pass. I'm not saying that's what is happening, but by using the BigDecimal structure like this, we are implicitly trusting that it's implementation is bug-free. Looking at it again, there is not a TON of complexity in BigDecimal, but it is still several hundred lines of arithmetic code with lots of branching.

Could I see the source from which it is based on?

@pentp
Copy link
Author

pentp commented Sep 18, 2017

I agree, the tests ensure that the results are the same as today in CoreRT/CoreCLR/Desktop, but not that they are correct. The source is in CoreRT or CoreCLR/oleaut32 (it's the same thing basically, in C# and C++), but I've tried to use as little as possible from these.

I think I was a bit too hasty to conclude that BigDecimal can't be made much simpler, have to investigate a couple of approaches...

@pentp
Copy link
Author

pentp commented Sep 18, 2017

In fact, I did find a bug in VarDecDiv when doing optimization work on CoreRT - it looks like shift with carry is done here using > not >= to detect overflow, but it's very unlikely to be hit (rgulRem[0] must be exactly 0x80000000). Same line in original C++ code also has this bug.

And another probable bug in Remainder, but this one looks like it's impossible to hit (but I haven't gotten around to proving that). This whole function is a good candidate for some major refactoring after the core decimal code is shared between CoreRT and CoreCLR.

@danmoseley
Copy link
Member

If the tests were split across two classes, I believe xunit may parallelize, potentially cutting the time taken.

@mellinoe please see responses above.

@pentp
Copy link
Author

pentp commented Sep 25, 2017

I simplified BigDecimal.Div significantly so it should be relatively straightforward now with only 4 branches.
Splitting the tests into separate classes did reduce the time taken by half.

@mellinoe
Copy link
Contributor

Looks okay to me now. @joperezr Did you want to take a look?

Copy link
Member

@joperezr joperezr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! Thanks for adding these @pentp

@joperezr
Copy link
Member

Merging now since the failing test in opensuse is not related (System.Drawing test)

@joperezr joperezr merged commit d235612 into dotnet:master Sep 25, 2017
@pentp pentp deleted the DecimalBigIntegerTests branch September 25, 2017 22:57
@karelz karelz added this to the 2.1.0 milestone Oct 11, 2017
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
* BigInteger based random testing of Decimal

* Comments for random testing of Decimal

* Parallelize random testing of Decimal

* Simplified BigDecimal.Div for Decimal tests


Commit migrated from dotnet/corefx@d235612
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants