This isn't technically a bug in intx. This issue is caused by a bug in the MSVC compiler, and seems to be fixed in very recent versions of MSVC. IMO it would be worth fixing in intx, but not required. This is still a useful FYI to anyone else who runs into this.
On MSVC, intx::addc(uint64_t, uint64_t, bool) returns carry == false when it should return true, if both operands are compile-time constants and optimizations are on.
auto r = intx::addc(0xFFFF'FFFF'FFFF'FFFFull, 1ull, false);
// expected: r.value == 0, r.carry == true
// actual (/O1, /O2, /O2 /GL): r.value == 0, r.carry == false
// without optimizations, output is correct.
This bug ultimately flows through to cause more serious issues like this:
uint128(2^64 - 1) + 1 returning 0 instead of 2^64.
Originally reproduced on MSVC 19.40.33812 (VS 2022 Community). Using godbolt.org, (https://godbolt.org/z/514rxdor9) you can check which compiler versions & optimization flags have the error, and which don't.
Looks like x64 MSVC 19.44 and newer works correctly, and arm64 MSVC 19.50 and newer works correctly.
Our fix was to replace return {t, carry1 || carry2} with return {t, static_cast<bool>( carry1 | carry2 )} in addc and replace return {e, carry1 || carry2} with return {e, static_cast<bool>( carry1 | carry2 )} in subc.
I didn't exhaustively check the rest of intx for similar issues. I came across this while migrating off of boost multiprecision.
This isn't technically a bug in intx. This issue is caused by a bug in the MSVC compiler, and seems to be fixed in very recent versions of MSVC. IMO it would be worth fixing in intx, but not required. This is still a useful FYI to anyone else who runs into this.
On MSVC,
intx::addc(uint64_t, uint64_t, bool)returnscarry == falsewhen it should returntrue, if both operands are compile-time constants and optimizations are on.This bug ultimately flows through to cause more serious issues like this:
uint128(2^64 - 1) + 1returning 0 instead of 2^64.Originally reproduced on MSVC 19.40.33812 (VS 2022 Community). Using godbolt.org, (https://godbolt.org/z/514rxdor9) you can check which compiler versions & optimization flags have the error, and which don't.
Looks like x64 MSVC 19.44 and newer works correctly, and arm64 MSVC 19.50 and newer works correctly.
Our fix was to replace
return {t, carry1 || carry2}withreturn {t, static_cast<bool>( carry1 | carry2 )}inaddcand replacereturn {e, carry1 || carry2}withreturn {e, static_cast<bool>( carry1 | carry2 )}insubc.I didn't exhaustively check the rest of intx for similar issues. I came across this while migrating off of boost multiprecision.