How We Found It
The computation involved converting values from a high-precision intermediate representation into GMP arbitrary-precision integers, then using those integers in further computation. The intermediate values routinely reach magnitudes of 3-4 billion — well beyond the 32-bit signed range.
We isolated the bug by forcing the computation to run exactly once (bypassing its internal retry loop) and comparing the full output byte-by-byte across platforms. The first portion matched perfectly. The divergence started at exactly the point where values were converted to GMP integers via mpz_set_si:
Linux: g[0] = -3784023924 (correct)
Windows: g[0] = -2147483648 (wrong - INT32_MIN)
The Official Workaround
GMP’s recommended solution is mpz_import and mpz_export, which operate on raw byte arrays with explicit size parameters:
static inline void mpz_set_int64(mpz_t rop, int64_t op) {
if (op >= 0) {
uint64_t u = (uint64_t)op;
mpz_import(rop, 1, -1, sizeof(uint64_t), 0, 0, &u);
} else {
uint64_t u = (uint64_t)(-op);
mpz_import(rop, 1, -1, sizeof(uint64_t), 0, 0, &u);
mpz_neg(rop, rop);
}
}
static inline int64_t mpz_get_int64(const mpz_t op) {
uint64_t u = 0;
mpz_export(&u, NULL, -1, sizeof(uint64_t), 0, 0, op);
if (mpz_sgn(op) < 0)
return -(int64_t)u;
return (int64_t)u;
}
Code taken from:
https://www.raykzhao.phd/2026/03/21/gmp-long-type-bug.html
:-)
How We Found It
The computation involved converting values from a high-precision intermediate representation into GMP arbitrary-precision integers, then using those integers in further computation. The intermediate values routinely reach magnitudes of 3-4 billion — well beyond the 32-bit signed range.
We isolated the bug by forcing the computation to run exactly once (bypassing its internal retry loop) and comparing the full output byte-by-byte across platforms. The first portion matched perfectly. The divergence started at exactly the point where values were converted to GMP integers via mpz_set_si:
Linux: g[0] = -3784023924 (correct)
Windows: g[0] = -2147483648 (wrong - INT32_MIN)
The Official Workaround
GMP’s recommended solution is mpz_import and mpz_export, which operate on raw byte arrays with explicit size parameters:
static inline void mpz_set_int64(mpz_t rop, int64_t op) {
if (op >= 0) {
uint64_t u = (uint64_t)op;
mpz_import(rop, 1, -1, sizeof(uint64_t), 0, 0, &u);
} else {
uint64_t u = (uint64_t)(-op);
mpz_import(rop, 1, -1, sizeof(uint64_t), 0, 0, &u);
mpz_neg(rop, rop);
}
}
static inline int64_t mpz_get_int64(const mpz_t op) {
uint64_t u = 0;
mpz_export(&u, NULL, -1, sizeof(uint64_t), 0, 0, op);
if (mpz_sgn(op) < 0)
return -(int64_t)u;
return (int64_t)u;
}
Code taken from:
https://www.raykzhao.phd/2026/03/21/gmp-long-type-bug.html
:-)