Home > OS >  Unable to work with gmp and uint64_t under windows in a sane manner
Unable to work with gmp and uint64_t under windows in a sane manner

Time:01-08

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.

  • Related