The following code works fine (compiles, assert statement gives true) under Linux/Debian (g 10.2), but refuses to work under Windows (mingw64 g 12.2.0). It's basically just supposed to assign UINT64_MAX
to a mpf_class
from the gmp library, but it seems gmp's mpf_class
defines no constructor and no operators for uint64_t
, which is unsigned long long int in the windows environment I'm trying to work with here. I bet there is a simple workaround I'm not seeing right now, because it's friday and late, correct?
#include <gmpxx.h> // link with gmp gmpxx
#include <string>
/// Helper to convert mpf_class to std::string
[[nodiscard]] inline std::string mpf_class_to_str(const mpf_class& num) {
mp_exp_t exponent(1);
return num.get_str(exponent, 10);
}
int main() {
mpf_class y = UINT64_MAX; // defined in my mingw64 as 0xffffffffffffffffULL
// ^
// | adding no cast -> compile error
// adding (unsigned int) cast -> compiles, but assertion below false
// adding (uint64_t) cast -> compile error
y *= UINT64_MAX; // UINT64_MAX²
assert(mpf_class_to_str(y) == "340282366920938463426481119284349108225");
}
The compile error g gives me is:
error: conversion from 'long long unsigned int' to 'mpf_class' is ambiguous
31 | mpf_class y = UINT64_MAX;
| ^~~~~~~~~~
and then it lists all candidates, which basically are (taken from gmpxx.h
instead of the error log):
#define __GMPXX_DEFINE_ARITHMETIC_CONSTRUCTORS \
__gmp_expr(signed char c) { init_si(c); } \
__gmp_expr(unsigned char c) { init_ui(c); } \
__gmp_expr(signed int i) { init_si(i); } \
__gmp_expr(unsigned int i) { init_ui(i); } \
__gmp_expr(signed short int s) { init_si(s); } \
__gmp_expr(unsigned short int s) { init_ui(s); } \
__gmp_expr(signed long int l) { init_si(l); } \
__gmp_expr(unsigned long int l) { init_ui(l); } \
__gmp_expr(float f) { init_d(f); } \
__gmp_expr(double d) { init_d(d); }
If I manipulate that header adding the line __gmp_expr(unsigned long long int l) { init_ui(l); }
all is well except this is too dirty of a fix even for me.
CodePudding user response:
mpz_class
cannot be constructed from a (unsigned) long long
. That is also stated in the documentation.
The library does not support (unsigned) long long
. See the mailing list thread here (and several older ones). In the answer Marc Glisse also points out that this is mainly an issue on Windows.
He also suggested a patch for support of the C initialization/conversion part of the problem here, but I couldn't find it on gmp's public development repository yet. As he points out in the message, this would not cover arithmetic operations and such though.
CodePudding user response:
Besides patching the gmp library as suggested in user17732522's answer it's also possible to get my example to work by using a conversion helper function. Something like this:
/// Helper for converting anything to mpf_class.
namespace bigdec {
template<typename T> [[nodiscard]] inline
mpf_class get(const T& num) {
std::stringstream ss;
ss << num;
mpf_class bigdec;
bigdec.set_str(ss.str(), 10);
return bigdec;
}}
And changing the usage accordingly:
int main() {
mpf_class y = bigdec::get(UINT64_MAX); // <- here
y *= bigdec::get(UINT64_MAX); // <- and here
assert(mpf_class_to_str(y) == "340282366920938463426481119284349108225");
}
Now it compiles and the assertion gives true in both - my Linux and Windows environment.