...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
All of the number types that are based on number
have certain conversion rules in common. In particular:
Any number type can be constructed (or assigned) from any builtin arithmetic type, as long as the conversion isn't lossy (for example float to int conversion):
cpp_dec_float_50 df(0.5); // OK construction from double cpp_int i(450); // OK constructs from signed int cpp_int j = 3.14; // Error, lossy conversion.
A number can be explicitly constructed from an arithmetic type, even when the conversion is lossy:
cpp_int i(3.14); // OK explicit conversion i = static_cast<cpp_int>(3.14) // OK explicit conversion i.assign(3.14); // OK, explicit assign and avoid a temporary from the cast above i = 3.14; // Error, no implicit assignment operator for lossy conversion. cpp_int j = 3.14; // Error, no implicit constructor for lossy conversion.
A number
can be converted
to any built in type, via the convert_to
member function:
mpz_int z(2); int i = z.convert_to<int>(); // sets i to 2
Additional conversions may be supported by particular backends.
A number
can be converted
to any built in type, via an explicit conversion operator: this functionality
is only available on compilers supporting C++11's explicit conversion
syntax.
mpz_int z(2); int i = z; // Error, implicit conversion not allowed. int j = static_cast<int>(z); // OK explicit conversion.
Any number type can be explicitly constructed (or
assigned) from a const char*
or a std::string
:
// pi to 50 places from a string: cpp_dec_float_50 df("3.14159265358979323846264338327950288419716939937510"); // Integer type will automatically detect "0x" and "0" prefixes and parse the string accordingly: cpp_int i("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000"); // Invalid input always results in a std::runtime_error being thrown: i = static_cast<cpp_int>("3.14"); // implicit conversions from strings are not allowed: i = "23"; // Error, no assignment operator for implicit conversion from string // assign member function, avoids having to create a temporary via a static_cast: i.assign("23"); // OK
Any number type will interoperate with the builtin types in arithmetic expressions as long as the conversions are not lossy:
// pi to 50 places from a string: cpp_dec_float_50 df = "3.14159265358979323846264338327950288419716939937510"; // Multiply by 2 - using an integer literal here is usually more efficient // than constructing a temporary: df *= 2; // You can't mix integer types with floats though: cpp_int i = 2; i *= 3.14; // Error, no *= operator will be found.
Any number type can be streamed to and from the C++ iostreams:
cpp_dec_float_50 df = "3.14159265358979323846264338327950288419716939937510"; // Now print at full precision: std::cout << std::setprecision(std::numeric_limits<cpp_dec_float_50>::max_digits10) << df << std::endl cpp_int i = 1; i <<= 256; // Now print in hex format with prefix: std::cout << std::hex << std::showbase << i << std::endl;
Interconversions between number types of the same family are allowed and are implicit conversions if no loss of precision is involved, and explicit if it is:
int128_t i128 = 0; int266_t i256 = i128; // OK implicit widening conversion i128_t = i256; // Error, no assignment operator found, narrowing conversion is explicit. i128_t = static_cast<int128_t>(i256); // OK, explicit narrowing conversion. mpz_int z = 0; mpf_float f = z; // OK, GMP handles this conversion natively, and it's not lossy and therefore implicit. mpf_float_50 f50 = 2; f = f50; // OK, conversion from fixed to variable precision, f will have 50 digits precision. f50 = f; // Error, conversion from variable to fixed precision is potentially lossy, explicit cast required.
Some interconversions between number types are completely generic, and are always available, albeit the conversions are always explicit:
cpp_int cppi(2); // We can always convert between numbers of the same category - // int to int, rational to rational, or float to float, so this is OK // as long as we use an explicit conversion: mpz_int z(cppi); // We can always promote from int to rational, int to float, or rational to float: cpp_rational cppr(cppi); // OK, int to rational cpp_dec_float_50 df(cppi); // OK, int to float df = static_cast<cpp_dec_float_50>(cppr); // OK, explicit rational to float conversion // However narrowing and/or implicit conversions always fail: cppi = df; // Compiler error, conversion not allowed
Other interconversions may be allowed as special cases, whenever the backend allows it:
mpf_t m; // Native GMP type. mpf_init_set_ui(m, 0); // set to a value; mpf_float i(m); // copies the value of the native type.
More information on what additional types a backend supports conversions from are given in the tutorial for each backend. The converting constructor will be implicit if the backend's converting constructor is also implicit, and explicit if the backends converting constructor is also explicit.