GMP (the GNU MP library) is a widely known library for arbitrary precision arithmetic on integers, rational numbers and floating-point numbers. When using GMP in C/C++ projects, one must face some subtleties.
According to GMP's official documentation:
All declarations needed to use GMP are collected in the include file gmp.h. It is designed to work with both C and C++ compilers.
#includeNote however that prototypes for GMP functions with FILE * parameters are only provided if
is included too. #include#include Likewise
(or ) is required for prototypes with va_list parameters, such as gmp_vprintf. And for prototypes with struct obstack parameters, such as gmp_obstack_printf, when available.
(I wonder why GMP doesn't include these required header files itself?)
When using GMP in C++, there are two API for choice: the original C API and the C++ wrapper API.
For C++ projects, the C++ wrapper API is very handy, but sometimes we still need the C API. For example: our main project may be written in C, but we found out that the GTest C++ library is very handy to write unit test cases. Then we need to use GMP C API in our GTest test cases.
In order to do so, first we must obey all the conventions above. Further more,
#includeextern "C" { #include #include }
Note: only include
When using the C++ wrapper API, although classes like mpz_class already overloaded various operators, there're still some subtleties to overcome when we need to treat mpz_class as builtin integer types. For example, using mpz_class with Boost.Rational:
#include
#include rational.hpp>
< span>
int main (int argc, char* argv [])
{
typedef boost::rational<mpz_class> rational;
rational r (mpz_class (0), mpz_class (1));
return 0;
}
Compile this program with clang++ (g++ error reporting sucks) and you'll see:
/usr/include/boost/rational.hpp|127 col 5| error: implicit instantiation of undefined template 'boost::STATIC_AS SERTION_FAILURE' BOOST_STATIC_ASSERT( ::std::numeric_limits ::is_specialized); ^
clearly, we need to specialize the std::numeric_limits
#includenamespace std { template struct numeric_limits<mpz_class> { public: static const bool is_specialized = true; static mpz_class min() throw() { return mpz_class(0u); } static mpz_class max() throw() { return mpz_class(0u); } static const int digits = 0; static const int digits10 = 0; static const bool is_signed = true; static const bool is_integer = true; static const bool is_exact = true; static const int radix = 2; static mpz_class epsilon() throw() { return mpz_class(0u); } static mpz_class round_error() throw() { return mpz_class(0u); } static const int min_exponent = 0; static const int min_exponent10 = 0; static const int max_exponent = 0; static const int max_exponent10 = 0; static const bool has_infinity = false; static const bool has_quiet_NaN = false; static const bool has_signaling_NaN = false; static const float_denorm_style has_denorm = denorm_absent; static const bool has_denorm_loss = false; static mpz_class infinity() throw() { return mpz_class(0U); } static mpz_class quiet_NaN() throw() { return mpz_class(0u); } static mpz_class signaling_NaN() throw() { return mpz_class(0u); } static mpz_class denorm_min() throw() { return mpz_class(0u); } static const bool is_iec559 = false; static const bool is_bounded = false; static const bool is_modulo = false; static const bool traps = false; static const bool tinyness_before = false; static const float_round_style round_style = round_toward_zero; }; // class numeric_limits } // namespace std
With this template specialization, the code above can be compiled successfully (don't forget to link libgmp and libgmpxx).
The second gotcha is that, GMP C++ classes are designed to support templated expressions. Suppose m and n are two mpz_class instances, then m + n will end up to be a certain expression template type internal to