Construction, conversion and assignment

All of mp++’s multiprecision classes default-construct to zero:

int_t n;
assert(n == 0);

rat_t q;
assert(q == 0);

real128 r128;
assert(r128 == 0);

real r;
assert(r == 0);

All of mp++’s multiprecision classes support a uniform style of initialisastion and conversion based on curly brackets. Using the same syntax, it is possible to:

  • initialise multiprecision objects from objects of (most of) C++’s numerical types (see the CppInteroperable concept for a full list),

  • initialise multiprecision objects from multiprecision objects of a different type,

  • initialise C++ numerical objects from multiprecision objects.

Let’s see a few examples:

int_t n{42};
assert(n == 42);
int m{n};
assert(m == 42);

In the code above, we are creating a multiprecision integer n from the C++ int literal 42. We are then converting n back to int, and checking that the converted value is the original one. As a general rule, mp++ will strive to preserve the exact input value during construction and conversion. If this is not possible, what happens next depends on the types and values involved. For instance, initialising an integer with a floating-point value results in truncation:

int_t n{1.23};
assert(n == 1);

In a similar fashion, initialising an integer with a rational also results in truncation:

int_t n{rat_t{-7, 3}};
assert(n == -2);

integer and rational cannot represent non-finite values, thus construction from such values will raise an exception:

int_t n{std::numeric_limits<double>::infinity()};  // Raises std::domain_error.
rat_t q{std::numeric_limits<double>::quiet_NaN()}; // Raises std::domain_error.

Construction of C++ integrals from integer and rational might fail in case of overflow, and it will produce the truncated value when constructing from rational:

int n{int_t{1} << 1024};         // int construction from very large value,
                                 // raises std::overflow_error.
assert((int{rat_t{4, 3}} == 1)); // int construction from rational truncates.

On the other hand, conversion of integer objects to C++ floating-point types does not raise any error even if it does not preserve the exact value:

float f{int_t{"32327737199221993919239912"}}; // Constructs a single-precision approximation
                                              // of the original integer.

The documentation of the multiprecision classes explains in detail the behaviour during construction and conversion.

All of mp++’s multiprecision classes can also be initialised from string-like entities (see the StringType concept for a full list). By default, string input is interpreted as the base-10 representation of the desired value, and parsing follows (hopefully) intuitive rules:

assert(int_t{"-42"} == -42)
assert(rat_t{"3/2"} == 1.5)
assert(real128{"2.5"} == 2.5);
assert((real{"-3.125E-2", 100} == -0.03125));

Note that for real we need to provide the precision as an additional parameter when constructing from string (in this specific example, 100 bits of precision are used). Depending on the multiprecision class, additional string constructors are available which allow to specify a different base for the representation of the value:

assert((int_t{"-101010", 2} == -42))          // Base 2.
assert((rat_t{"2a/1c", 16} == 1.5))           // Base 16.
assert((real{"7B.1", 32, 100} == 235.03125)); // Base 32, 100 bits of precision.

It is of course also possible to assign values to already-constructed multiprecision objects. In general, the behaviour of the assignment operators mirrors the behaviour of the corresponding constructors. For instance:

int_t n{1};
n = 42;
assert(n == 42);
n = -3.7;
assert(n == -3);
n = "-128";
assert(n == -128);
n = std::numeric_limits<double>::quiet_NaN(); // Raises std::domain_error.

rat_t q{3, 4};
q = 1.5;
assert((q == rat_t{3, 2}));
q = int_t{10};
assert(q == 10);
q = "-5/6";
assert((q == rat_t{-5, 6}));
q = std::numeric_limits<double>::infinity();  // Raises std::domain_error.

Note however that, due to language limitations, it is not possible to assign a multiprecision value to a C++ numerical object. Explicitly casting the multiprecision value before the assignment will however work:

int n = 5;
n = int_t{42};                   // This will NOT compile.
n = static_cast<int>(int_t{42}); // This will compile.
assert(n == 42);