Reputation: 383
I'm working with a protocol where I don't have control of the input types. But I need to compute the difference in two, 64-bit unsigned integers (currently baked into a std::uint64_t
). But the difference might be negative or positive. I don't want to do this:
uint64_t a{1};
uint64_t b{2};
int64_t x = a - b; // -1; correct, but what if a and b were /enormous/?
So I was looking at Boost's safe_numerics
here. The large-values case is handled as I would like:
boost::safe_numerics::safe<uint64_t> a{UINT64_MAX};
boost::safe_numerics::safe<uint64_t> b{1};
boost::safe_numerics::safe<int64_t> x = a - b;
// ^^ Throws "converted unsigned value too large: positive overflow error"
Great! But ... they're a little too safe:
boost::safe_numerics::safe<uint64_t> a{1}; //UINT64_MAX;
boost::safe_numerics::safe<uint64_t> b{2};
boost::safe_numerics::safe<int64_t> x = a - b;
// ^^ Throws "subtraction result cannot be negative: negative overflow error"
// ... even though `x` is signed
I have a suspicion that it's a - b
that actually throws, not the assignment. But I've tried every kind of cast in the book to get a - b
into a safe, signed integer, but no joy.
There are some inelegant ways to deal with this, like comparing a
and b
to always subtract the smaller from the larger. Or I can do a lot of casting with boost::numeric_cast
, or old-school range checking. Or...god forbid...I just throw myself when a
or b
exceed 63 bits, but all that is a bit lame.
But my real question is: Why does Boost detect a negative overflow in the final example above? Am I using safe_numerics
incorrectly?
Am targeting C++-17 with gcc on a 64-bit system and using Boost 1.71.
Upvotes: 0
Views: 303
Reputation: 383
The behavior I was looking for is actually implemented in boost::safe_numerics::checked_result
:
https://www.boost.org/doc/libs/develop/libs/safe_numerics/doc/html/checked_result.html
checked::subtract
allows negative overflows when the difference of two unsigned integers is negative (and being stored in a signed integer of adequate size). But it throws when the result does not. For example:
using namespace std;
using namespace boost::safe_numerics;
safe<uint64_t> a{2};
safe<uint64_t> b{1};
checked_result<int64_t> x0 = checked::subtract<int64_t>(b, a);
assert(x0 == -1);
checked_result<int64_t> x1 = checked::subtract<int64_t>(a, b);
assert(x1 == 1);
a = UINT64_MAX;
checked_result<int64_t> x2 = checked::subtract<int64_t>(a, b); // throws
Upvotes: 2