Skip to content

Port number's mantissa to std::vector#544

Closed
janisozaur wants to merge 7 commits intomicrosoft:masterfrom
janisozaur:mantissa
Closed

Port number's mantissa to std::vector#544
janisozaur wants to merge 7 commits intomicrosoft:masterfrom
janisozaur:mantissa

Conversation

@janisozaur
Copy link
Copy Markdown
Contributor

These are the changes extracted from #211 that port number's mantissa
from flexible member arrays, a non-standard C++ extension, to
std::vector, which enables compilation and linkage with clang.

@fwcd is the original author of these changes, I (@janisozaur) merely
rebased them on top of current master.

The rebase was luckily fairly straight-forward, with the only real
conflict coming from 7a7ceb5
(#412), but was trivial to
fix.


// Loop over all the relative digits from MSD to LSD
for (ptr = &(a->mant[a->cdigit - 1]); cdigits > 0; ptr--, cdigits--)
for (ptr = &(a.mant[a.cdigit - 1]); cdigits > 0; ptr--, cdigits--)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The unit tests are failing, and I think the problem is here--we're calling a.mant[-1].

One reasonable fix would be to rewrite this as a while loop so we check cdigits first:

while(cdigits > 0)
{
ptr = &(a.mant[a.cdigit - 1]);
// etc.
ptr--;
cdigits--;
}

@HowardWolosky HowardWolosky added the codebase quality Issues that are not bugs, but still might be worth improving (eg, code hygiene or maintainability) label Jun 10, 2019
@janisozaur
Copy link
Copy Markdown
Contributor Author

I rebased this on top of already approved #545 to preempt minor conflict and fixed some issues I found. This might address the warnings raised previously, I'd like to hear back from the test suite, would be nice to have it fixed

@mcooley
Copy link
Copy Markdown
Member

mcooley commented Jun 10, 2019

/azp run

@azure-pipelines
Copy link
Copy Markdown

Pull request contains merge conflicts.

@mcooley
Copy link
Copy Markdown
Member

mcooley commented Jun 10, 2019

Sorry for the test pipeline outage earlier. The unit tests are still failing with the same issue, unfortunately.

@janisozaur janisozaur force-pushed the mantissa branch 2 times, most recently from 5b5046f to ce8d263 Compare June 11, 2019 21:12
@janisozaur
Copy link
Copy Markdown
Contributor Author

Judging by the error message reported, the tests are failing because of infrastructure still.

artifact-engine: unhandled rejection Failed to extract package with error Error: ENOENT: no such file or directory, open 'D:\_work\1\a\drop.zip'

@mcooley
Copy link
Copy Markdown
Member

mcooley commented Jun 11, 2019

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@mcooley
Copy link
Copy Markdown
Member

mcooley commented Jun 12, 2019

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@mcooley
Copy link
Copy Markdown
Member

mcooley commented Jun 12, 2019

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@mcooley
Copy link
Copy Markdown
Member

mcooley commented Jun 12, 2019

Still working on this CI infrastructure issue--I'm waiting for another team to investigate.

I tried running the unit tests locally, and I'm still seeing failures. The next one I see is a vector subscript out of range in mulnumx, line 42. There wasn't an obvious logic change there, so a deeper investigation will be needed.

@janisozaur
Copy link
Copy Markdown
Contributor Author

How can I get size of mantissa in its previous (flexible array) form? The equivalent of num.mant.size() in the new form? What is the value of num->mant[0] in previous form, when default-initialised? Is that a legal access?

throw(CALC_E_INVALIDRANGE);
}
return (pnumret);
NUMBER num = NUMBER();
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This would create a default-constructed value, which will have sign equal 1:

int32_t sign = 1; // The sign of the mantissa, +1, or -1

That's unlike the current/old code, which would initialise it to 0 (via zmalloc call, which calls calloc underneath).

{
// Remove them.
memmove(pnum->mant, pmant, (int)(cdigits * sizeof(MANTTYPE)));
copy(pmant, pmant + cdigits, pnum.mant.begin());
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In case the elements overlap, the result of std::copy is undefined, unlike with memmove

if (c.mant.begin() != ++ptrc)
{
destroynum(num);
copy(ptrc, ptrc + cdigits, c.mant.begin());
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

memmove vs std::copy again

// radix being used.
int32_t exp = 0; // The offset of digits from the radix point
// (decimal point in radix 10)
std::vector<MANTTYPE> mant;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It seems cdigit is used as count of entries in the flexible array in the long form, is that correct? Additionally, cdigits usage seems to suggest such arrays used to have at least one element defaulting to zero, is that correct? Since vector by default doesn't contain any values, perhaps default-initialising it with a single zero would address the current problems:

Suggested change
std::vector<MANTTYPE> mant;
std::vector<MANTTYPE> mant{0};

@janisozaur
Copy link
Copy Markdown
Contributor Author

@mcooley I highlighted some possible differences, could you review them?

@mcooley
Copy link
Copy Markdown
Member

mcooley commented Jun 13, 2019

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@mcooley
Copy link
Copy Markdown
Member

mcooley commented Jun 13, 2019

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@mcooley
Copy link
Copy Markdown
Member

mcooley commented Jun 13, 2019

Okay, looks like we’ve finally worked around the CI infrastructure issues.

Thanks for flagging all these spots to double-check. I am about to head out on vacation for a few days, but I will take a closer look at these next week.

@janisozaur
Copy link
Copy Markdown
Contributor Author

@mcooley is there any way I can get Azure to produce actual error messages, instead of the rather unhelpful

Error Message:
 Exception Code: C0000005

?

{
m_mantissa.reserve(p->cdigit);
copy(p->mant, p->mant + p->cdigit, back_inserter(m_mantissa));
m_mantissa.reserve(p.cdigit);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why not use resize and thence avoid back_inserter ?

@janisozaur janisozaur force-pushed the mantissa branch 2 times, most recently from dc1e229 to 000733d Compare June 20, 2019 18:49
@janisozaur
Copy link
Copy Markdown
Contributor Author

All the CI runs are green now! @mcooley can you review now?

Copy link
Copy Markdown

@danbelcher-MSFT danbelcher-MSFT left a comment

Choose a reason for hiding this comment

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

Thanks for getting this working! My main concern with this change is around performance and clarity of the codebase. Previously we were passing and manipulating everything as PNUMBER and now we've switched to NUMBER. In some cases, there is no performance loss, such as changing from PNUMBER to NUMBER &, but I'm generally concerned that we may have introduced additional copies etc that weren't there previously. There are a number of places where PNUMBER was changed to NUMBER* but could have been changed to NUMBER& for increased safety (I recognize it's a cascading change that is outside the scope of your work here, but it's worth bringing up). There's also some confusion in the codebase in the distinction between Number and NUMBER, as well as the naming of data members (pq is not a pointer). Number is intended to be the modern replacement for the NUMBER POD. Hopefully we see the codebase start to move functionality into the Number/Rational classes and we can drop NUMBER/RAT altogether.

Number::Number(PNUMBER p) noexcept
: m_sign{ p->sign }
, m_exp{ p->exp }
Number::Number(NUMBER p) noexcept
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
Number::Number(NUMBER p) noexcept
explicit Number::Number(const NUMBER& p) noexcept

}

PNUMBER Number::ToPNUMBER() const
NUMBER Number::ToNUMBER() const
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
NUMBER Number::ToNUMBER() const
explicit Number::operator NUMBER() const

*ptrRet++ = digit;
}

NUMBER ret = _createnum(static_cast<uint32_t>(this->Mantissa().size()) + 1);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can we get rid of _createnum and use a constructor instead?

// Mantissa specified, convert to number form.
PNUMBER pnummant = StringToNumber(mantissa, radix, precision);
if (pnummant == nullptr)
optional<NUMBER> pnummant = StringToNumber(mantissa, radix, precision);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

std::optional works well with auto and pointer semantics:

Suggested change
optional<NUMBER> pnummant = StringToNumber(mantissa, radix, precision);
auto pnummant = StringToNumber(mantissa, radix, precision);
if (!pnummant)
{
...
}

}

if (round != nullptr)
if (round.has_value())
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
if (round.has_value())
if (round)

destroynum(tmp);
tmp = lasttmp;
lasttmp = nullptr;
lasttmp = optional<NUMBER>();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
lasttmp = optional<NUMBER>();
lasttmp = nullopt;

{
PNUMBER pp;
PNUMBER pq;
NUMBER pp;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
NUMBER pp;
NUMBER p;
NUMBER q;

The names pp and pq are shorthand for pointer to p and pointer to q. There will be a lot of change in the codebase if you rename these, but we're also adding confusion now that the names have embedded type information that isn't actually correct.

OutputDebugString(outputString.str().c_str()); \
} \
_destroynum(x), (x) = nullptr
{
Copy link
Copy Markdown

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 is actually valid?

@janisozaur
Copy link
Copy Markdown
Contributor Author

I've rebased it and updated to include changes required after #498, but haven't addressed the review comments yet.

@janisozaur
Copy link
Copy Markdown
Contributor Author

I started writing some benchmarks using google benchmark, but have never really finished them to a point where they would be worth submitting and eventually lost interest in that, as other things took my time away. I can't say I fully agree with the argument about possible performance loss, when there is no benchmark available in the upstream project, so I will simply address the review comments as they are now.

@janisozaur janisozaur force-pushed the mantissa branch 2 times, most recently from 6b39c64 to 9d0cec9 Compare December 10, 2019 22:32
fwcd and others added 7 commits December 10, 2019 23:33
These are the changes extracted from microsoft#211 that port number's mantissa
from flexible member arrays, a non-standard C++ extension, to
std::vector, which enables compilation and linkage with clang.

@fwcd is the original author of these changes, I (@janisozaur) merely
rebased them on top of current master.

The rebase was luckily fairly straight-forward, with the only real
conflict coming from 7a7ceb5
(microsoft#412), but was trivial to
fix.
@mcooley
Copy link
Copy Markdown
Member

mcooley commented Mar 13, 2020

Closing this since it's become stale and we haven't had time to thoroughly investigate the regressions in our primary environment (Visual Studio on Windows). Thanks for your interest in working on portability; we hope that some of these changes can be contributed in smaller pieces in the future.

@mcooley mcooley closed this Mar 13, 2020
@janisozaur
Copy link
Copy Markdown
Contributor Author

I've rebased the code here to a branch 'mantissa-202110' in my fork. This is currently under testing, hopefully will submit another PR at some point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codebase quality Issues that are not bugs, but still might be worth improving (eg, code hygiene or maintainability)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants