-
Notifications
You must be signed in to change notification settings - Fork 112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug: Inconsistent Underflow Behavior for Unsigned Integers Greater Than 128 Bits #626
Comments
Out of curiosity, I decided to go ahead and test overflow conditions too. It also fails with inconsistent behavior. See the sample code: Sample Code#include <iostream>
#include <boost/multiprecision/cpp_int.hpp>
#include <limits>
template <unsigned int N>
void test_overflow() {
using uint_t = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N, N, boost::multiprecision::unsigned_magnitude, boost::multiprecision::cpp_int_check_type::checked, void>>;
uint_t value = std::numeric_limits<uint_t>::max();
std::cout << "Testing overflow for uint" << N << "_t..." << std::endl;
std::cout << "Value before: " << value << std::endl;
try {
value += 1;
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
std::cout << "Value after: " << value << std::endl;
if (value != std::numeric_limits<uint_t>::max()) {
std::cout << "Something wrong with size: " << N << std::endl;
}
}
template <unsigned int N>
struct TestOverflow {
static void run() {
test_overflow<N>();
TestOverflow<N + 8>::run();
}
};
template <>
struct TestOverflow<1032> {
static void run() {}
};
int main() {
TestOverflow<8>::run();
return 0;
} Output
It essentially fails for every bit size, with the following exceptions:
Interestingly, any bit size that is a multiple of 64 and greater than 128 (e.g., 192, 256, 320, etc.) returns 0 after an overflow attempt. For other bit sizes greater than 64, the overflow operation throws an exception as expected but then incorrectly increments the value, causing it to exceed std::numeric_limits::max(). These combined bugs point to a more significant underlying issue within the library. The inconsistent behavior when overflowing and underflowing values, along with different error messages depending on the bit size, suggests that the compiler might be selecting different templates based on the bit size. This discrepancy could be causing these issues, and other similar bugs might be hidden due to this underlying problem. Please investigate these discrepancies thoroughly. |
You are correct that two different templates are used: one for the regular multiprecision case, and one for the "trivial" case where the number of bits requested will fit inside a |
I haven't looked at the overflow case yet, but the underflow case appears to NOT be a bug, ie given:
Then a
If expression templates are off, then I'm sure there are other corner cases, those are just the ones I can think of. So in short, if an operation raises an exception, then the value of the target operand cannot be relied upon afterwards. I don't believe this is totally unreasonable. Indeed, one could argue that:
should leave x unchanged in the event of an exception. But that would be rather runtime expensive to implement. |
Thank you for the detailed explanation. I have a few follow-up questions and points to consider:
Is this behavior explicitly defined in the Boost Multiprecision documentation? I couldn't find any mention of this specific behavior. Clear documentation would help users understand the expected behavior and design their code accordingly.
While I understand the concern about runtime performance, it is important to consider the expectations of users who choose Exceptions are inherently expensive, but their use implies a focus on handling error conditions properly. In environments where |
Document that the value of the target operand is unspecified after a throw. Improve testing. Fixes #626
Description:
We have encountered inconsistent behavior when performing underflow operations on unsigned integers greater than 128 bits using Boost Multiprecision with
cpp_int_check_type::checked
. This issue does not occur with 128-bit numbers but starts failing for 136-bit numbers and above.Steps to Reproduce:
Sample Code:
Sample Code Output:
Observed Behavior
uint128_t
or smaller numbers, the subtraction operation correctly throws an exception: "Subtraction resulted in a negative value, but the type is unsigned" and the value remains0
.uint136_t
and larger, the subtraction operation throws an different exception: "Attempt to negate an unsigned number" but the value becomes 1, which is unexpected.Expected Behavior:
The value should remain
0
after the exception is caught, indicating that the underflow operation was unsuccessful.Additional Information
Reference to previous bug
It seems that this bug and the previous bug (#624) might be related due to the following similarities:
This suggests that other bugs might be hiding under different templates.
The text was updated successfully, but these errors were encountered: