Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More backports from #233 #234

Merged
merged 3 commits into from
Apr 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Changelog
New
~~~

- Add a move constructor with custom precision
for :cpp:class:`~mppp::real`
(`#234 <https://github.com/bluescarni/mppp/pull/234>`__).
- Add support for C++20's ``constinit``
(`#233 <https://github.com/bluescarni/mppp/pull/233>`__).
- Improve the interoperability between mp++ classes
Expand Down
13 changes: 11 additions & 2 deletions doc/real.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,22 @@ The real class
:param other: the construction argument.

.. cpp:function:: explicit real(const real &other, mpfr_prec_t p)
.. cpp:function:: explicit real(real &&other, mpfr_prec_t p)

Copy constructor with custom precision.
Copy/move constructors with custom precision.

This constructor will set *this* to a copy of *other* with precision *p*. If *p*
These constructors will set *this* to the value of *other* with precision *p*. If *p*
is smaller than the precision of *other*, a rounding operation will be performed,
otherwise the value will be copied exactly.

After move construction, the only valid operations on *other* are
destruction, copy/move assignment and the invocation of the :cpp:func:`~mppp::real::is_valid()`
member function. After re-assignment, *other* can be used normally again.

.. versionadded:: 0.20

The move overload.

:param other: the construction argument.
:param p: the desired precision.

Expand Down
2 changes: 2 additions & 0 deletions include/mp++/real.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ class MPPP_DLL_PUBLIC real

// Copy constructor with custom precision.
explicit real(const real &, ::mpfr_prec_t);
// Move constructor with custom precision.
explicit real(real &&, ::mpfr_prec_t);

// Constructor from a special value, sign and precision.
explicit real(real_kind, int, ::mpfr_prec_t);
Expand Down
15 changes: 15 additions & 0 deletions src/real.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,21 @@ real::real(const real &other, ::mpfr_prec_t p)
::mpfr_set(&m_mpfr, &other.m_mpfr, MPFR_RNDN);
}

// Move constructor with custom precision.
real::real(real &&other, ::mpfr_prec_t p)
{
// Check the precision first of all.
check_init_prec(p);

// Shallow copy other.
m_mpfr = other.m_mpfr;
// Mark the other as moved-from.
other.m_mpfr._mpfr_d = nullptr;

// Apply the precision.
prec_round_impl<false>(p);
}

// Construction from FPs.
template <typename Func, typename T>
void real::dispatch_fp_construction(const Func &func, const T &x)
Expand Down
71 changes: 55 additions & 16 deletions test/real_basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,29 +187,32 @@ TEST_CASE("real constructors")
REQUIRE(!::mpfr_equal_p(r6.get_mpfr_t(), real{1.3}.get_mpfr_t()));
REQUIRE(r6.get_prec() == 12);
}
REQUIRE_THROWS_PREDICATE((real{real{4}, -1}), std::invalid_argument, [](const std::invalid_argument &ex) {
return ex.what()
== "Cannot init a real with a precision of -1: the maximum allowed precision is "
+ detail::to_string(real_prec_max()) + ", the minimum allowed precision is "
+ detail::to_string(real_prec_min());
});
REQUIRE_THROWS_PREDICATE((real{real{4}, 0}), std::invalid_argument, [](const std::invalid_argument &ex) {
return ex.what()
== "Cannot init a real with a precision of 0: the maximum allowed precision is "
+ detail::to_string(real_prec_max()) + ", the minimum allowed precision is "
+ detail::to_string(real_prec_min());
});
if (real_prec_min() > 1) {
REQUIRE_THROWS_PREDICATE((real{real{4}, 1}), std::invalid_argument, [](const std::invalid_argument &ex) {
REQUIRE_THROWS_PREDICATE(
(real{static_cast<const real &>(real{4}), -1}), std::invalid_argument, [](const std::invalid_argument &ex) {
return ex.what()
== "Cannot init a real with a precision of -1: the maximum allowed precision is "
+ detail::to_string(real_prec_max()) + ", the minimum allowed precision is "
+ detail::to_string(real_prec_min());
});
REQUIRE_THROWS_PREDICATE(
(real{static_cast<const real &>(real{4}), 0}), std::invalid_argument, [](const std::invalid_argument &ex) {
return ex.what()
== "Cannot init a real with a precision of 1: the maximum allowed precision is "
== "Cannot init a real with a precision of 0: the maximum allowed precision is "
+ detail::to_string(real_prec_max()) + ", the minimum allowed precision is "
+ detail::to_string(real_prec_min());
});
if (real_prec_min() > 1) {
REQUIRE_THROWS_PREDICATE(
(real{static_cast<const real &>(real{4}), 1}), std::invalid_argument, [](const std::invalid_argument &ex) {
return ex.what()
== "Cannot init a real with a precision of 1: the maximum allowed precision is "
+ detail::to_string(real_prec_max()) + ", the minimum allowed precision is "
+ detail::to_string(real_prec_min());
});
}
if (real_prec_max() < detail::nl_max<::mpfr_prec_t>()) {
REQUIRE_THROWS_PREDICATE(
(real{real{4}, detail::nl_max<::mpfr_prec_t>()}), std::invalid_argument,
(real{static_cast<const real &>(real{4}), detail::nl_max<::mpfr_prec_t>()}), std::invalid_argument,
[](const std::invalid_argument &ex) {
return ex.what()
== "Cannot init a real with a precision of " + detail::to_string(detail::nl_max<::mpfr_prec_t>())
Expand All @@ -234,6 +237,42 @@ TEST_CASE("real constructors")
r8 = std::move(r8a);
REQUIRE(!r8a.is_valid());
REQUIRE(r8.is_valid());
// Move ctor with different precision.
REQUIRE(real{real{42}, 512}.get_prec() == 512);
REQUIRE(real{real{42}, 512} == 42);
REQUIRE(real{real{3}, real_prec_min()}.get_prec() == real_prec_min());
REQUIRE(real{real{1}, real_prec_min()} == 1);
real r8b{42};
real r8c{std::move(r8b), 123};
REQUIRE(!r8b.is_valid());
REQUIRE(r8c == 42);
REQUIRE(r8c.get_prec() == 123);
// Revive via move assignment.
r8b = real{56, 87};
REQUIRE(r8b.is_valid());
REQUIRE(r8b == 56);
REQUIRE(r8b.get_prec() == 87);
real r8d{std::move(r8b), 95};
REQUIRE(!r8b.is_valid());
REQUIRE(r8d == 56);
REQUIRE(r8d.get_prec() == 95);
// Revive via copy assignment.
r8b = r8d;
REQUIRE(r8b.is_valid());
REQUIRE(r8b == 56);
REQUIRE(r8b.get_prec() == 95);
REQUIRE_THROWS_PREDICATE((real{real{4}, -5}), std::invalid_argument, [](const std::invalid_argument &ex) {
return ex.what()
== "Cannot init a real with a precision of -5: the maximum allowed precision is "
+ detail::to_string(real_prec_max()) + ", the minimum allowed precision is "
+ detail::to_string(real_prec_min());
});
REQUIRE_THROWS_PREDICATE((real{real{4}, 0}), std::invalid_argument, [](const std::invalid_argument &ex) {
return ex.what()
== "Cannot init a real with a precision of 0: the maximum allowed precision is "
+ detail::to_string(real_prec_max()) + ", the minimum allowed precision is "
+ detail::to_string(real_prec_min());
});
// String constructors.
REQUIRE((::mpfr_equal_p(real{"123", 10, 100}.get_mpfr_t(), real{123}.get_mpfr_t())));
REQUIRE((::mpfr_equal_p(real{"123", 100}.get_mpfr_t(), real{123}.get_mpfr_t())));
Expand Down