2

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.

nada
  • 2,109
  • 2
  • 16
  • 23

2 Answers2

3

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.

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • So the only reason my example worked in linux world is because `unsigned long == unsigned long long` there – nada Jan 06 '23 at 23:27
  • 2
    @nada They are never the same type. If you tried to use `unsigned long long` on linux you would get the same result. However, it is more common to use types with explicit sizes and on linux (and most other platforms) the 64bit fixed-size types are `long` types, not `long long` ones. Windows decided to not make `long` 64bit though, so that doesn't work on Windows. – user17732522 Jan 06 '23 at 23:29
  • 2
    Thanks for the clarifying. With "==" I meant "are the same size", not the "same type", sorry for my sloppy wording there – nada Jan 06 '23 at 23:39
1

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.

nada
  • 2,109
  • 2
  • 16
  • 23