为什么来自 MSYS2 的 GNU MP (gmplib) 意外地将 ull 转换为 32 位整数?
Why GNU MP (gmplib) from MSYS2 unexpectadely converts ull to 32 bit integer?
我正在使用 MSYS2(64 位)将我的 C++ 代码从 Linux 移植到 Windows。密钥库是 GNU MP (gmplib)。以下代码在 MSYS2/MinGW64 环境中给出错误结果,而在 Ubuntu 环境中工作正常。显然,在使用 mpz_set_ui
函数时,存在从 64 位整数到 32 位非整数的不需要的转换。
#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 正确输出:
GMP version: 6.2.0
GMP_LIMB_BITS: 64
18446744073709551615
18446744073709551615
MSYS2/MinGW64 意外转换:
GMP version: 6.2.1
GMP_LIMB_BITS: 64
18446744073709551615
4294967295
重现该行为的步骤:
- 全新安装 MSYS2(64 位)。
- 在 MSYS2 环境中从
Getting Started
到 https://www.msys2.org/ 运行 的步骤:
pacman -Syu
pacman -Su
pacman -S --needed base-devel mingw-w64-x86_64-toolchain
- 使用静态链接在 MinGW64 环境中编译:
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 按预期指向 C:/msys64/mingw64/include/gmp.h,我验证了 C:\msys64\mingw64\lib\libgmp.a 用于链接,没有其他版本的 gmplib.a存在于 C:/msys64
的子文件夹中
我还使用以下方法自定义构建了 GNU MP 库:
./configure
make
make install
但是,不需要的转换保持不变。我可能会错过一些基本的东西,但我很茫然。这是我在 Whosebug 上的第一个问题,非常感谢您的帮助。
mpz_get_ui()
函数 returns 是 unsigned long
,不是 unsigned long long
。
在Linux/GCC上,unsigned long
是一个64位的值,但是C++标准只要求unsigned long
支持最大4'294'967'295
的值,可以满足一个 32 位整数。 Linux 允许更大的值,但 Windows 上的 MSVC 和 MinGW 将对 unsigned long
使用 32 位整数。这意味着在 Windows,您在 mps_set_ui()
中的 64 位输入值被降级为 32 位值。
因此,两个编译器的行为都是正确的,您被平台特定的实现细节绊倒了。
如果您希望允许在任何地方使用 64 位整数,您应该使用固定位宽的整数类型(例如 uint64_t
而不是 unsigned long
),但这仍然不允许不幸的是,你在 Windows 上指定 64 位整数 mp_set_ui
。
我正在使用 MSYS2(64 位)将我的 C++ 代码从 Linux 移植到 Windows。密钥库是 GNU MP (gmplib)。以下代码在 MSYS2/MinGW64 环境中给出错误结果,而在 Ubuntu 环境中工作正常。显然,在使用 mpz_set_ui
函数时,存在从 64 位整数到 32 位非整数的不需要的转换。
#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 正确输出:
GMP version: 6.2.0
GMP_LIMB_BITS: 64
18446744073709551615
18446744073709551615
MSYS2/MinGW64 意外转换:
GMP version: 6.2.1
GMP_LIMB_BITS: 64
18446744073709551615
4294967295
重现该行为的步骤:
- 全新安装 MSYS2(64 位)。
- 在 MSYS2 环境中从
Getting Started
到 https://www.msys2.org/ 运行 的步骤:
pacman -Syu
pacman -Su
pacman -S --needed base-devel mingw-w64-x86_64-toolchain
- 使用静态链接在 MinGW64 环境中编译:
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 按预期指向 C:/msys64/mingw64/include/gmp.h,我验证了 C:\msys64\mingw64\lib\libgmp.a 用于链接,没有其他版本的 gmplib.a存在于 C:/msys64
我还使用以下方法自定义构建了 GNU MP 库:
./configure
make
make install
但是,不需要的转换保持不变。我可能会错过一些基本的东西,但我很茫然。这是我在 Whosebug 上的第一个问题,非常感谢您的帮助。
mpz_get_ui()
函数 returns 是 unsigned long
,不是 unsigned long long
。
在Linux/GCC上,unsigned long
是一个64位的值,但是C++标准只要求unsigned long
支持最大4'294'967'295
的值,可以满足一个 32 位整数。 Linux 允许更大的值,但 Windows 上的 MSVC 和 MinGW 将对 unsigned long
使用 32 位整数。这意味着在 Windows,您在 mps_set_ui()
中的 64 位输入值被降级为 32 位值。
因此,两个编译器的行为都是正确的,您被平台特定的实现细节绊倒了。
如果您希望允许在任何地方使用 64 位整数,您应该使用固定位宽的整数类型(例如 uint64_t
而不是 unsigned long
),但这仍然不允许不幸的是,你在 Windows 上指定 64 位整数 mp_set_ui
。