Martin Zeman
Martin Zeman

Reputation: 58

Why GNU MP (gmplib) from MSYS2 unexpectadely converts ull to 32 bit integer?

I am porting my c++ code from Linux to Windows using MSYS2 (64 bit). The key library is GNU MP (gmplib). The following code gives wrong results in MSYS2/MinGW64 environment while in Ubuntu works fine. Apparantly there is an unwanted conversion from 64 bit integer to 32 bit unteger when using mpz_set_ui function.

#include <iostream>
    #include <gmp.h>  
    int main() {
        std::cout << "GMP version: " << __gmp_version << std::endl;
        std::cout << "GMP_LIMB_BITS: " << GMP_LIMB_BITS << std::endl;
    
        unsigned long long a = UINT64_MAX; 
        std::cout << a << std::endl;
    
        mpz_t mpz;
        mpz_init(mpz);
        mpz_set_ui(mpz, a);
    
        unsigned long long b = mpz_get_ui(mpz);
        std::cout << b << std::endl;
    }

Ubuntu correct output:

GMP version: 6.2.0
GMP_LIMB_BITS: 64
18446744073709551615
18446744073709551615

MSYS2/MinGW64 unexpected conversion:

GMP version: 6.2.1
GMP_LIMB_BITS: 64
18446744073709551615
4294967295

Steps to reproduce the behavior:

  1. Fresh installation of MSYS2 (64 bit).
  2. Steps from Getting Started from https://www.msys2.org/ run in MSYS2 environment:

pacman -Syu

pacman -Su

pacman -S --needed base-devel mingw-w64-x86_64-toolchain

  1. Compiling in MinGW64 environment using static linking:
g++ -MT .o/main.o -MD -MP -MF .d/main.Td -std=c++17 -g -Wall -Wextra -pedantic -c -o .o/main.o main.cpp

mv -f .d/main.Td .d/main.d

g++ -static -o main .o/main.o -lgmp

main.d points to C:/msys64/mingw64/include/gmp.h as expected and I verified that C:\msys64\mingw64\lib\libgmp.a is used for linking and no other version of gmplib.a is present in subfolders of C:/msys64

I also did custom build of GNU MP library using:

./configure

make

make install

However the unwanted conversion ramained the same. I probably miss something basic, but I am at a loss. This is my first question on Stackoverflow and I would really appreciate your help.

Upvotes: 4

Views: 258

Answers (1)

Karl Nicoll
Karl Nicoll

Reputation: 16419

The mpz_get_ui() function returns an unsigned long, NOT an unsigned long long.

On Linux/GCC, unsigned long is a 64-bit value, however the C++ standard only requires that unsigned long support values up to 4'294'967'295, which can be satisfied by a 32-bit integer. Linux allows larger values, but MSVC and MinGW on Windows will use a 32-bit integer for unsigned long. This means that on Windows, your 64-bit input value in mps_set_ui() is downgraded to a 32-bit value.

Therefore the behavior on both compilers is correct, you've tripped over a platform-specific implementation detail.

If you wish to allow use of 64-bit integers everywhere, you should use fixed bit-width integer types (e.g. uint64_t instead of unsigned long), but that stil won't allow you to specify 64-bit integers mp_set_ui on Windows unfortunately.

Upvotes: 3

Related Questions