On 30/10/13 10:00, Kai Tietz wrote:
> 2013/10/30 Andrew Haley <[email protected]>:
>> On 10/30/2013 08:34 AM, Ondřej Bílka wrote:
>>
>>>>
>>> The reasons of adding builtins is performance. Without that one can
>>> write a simple template to generically check overflows like
>>>
>>> template <class C> class overflow {
>>> public:
>>> C val;
>>> overflow <C> operator + (overflow <C> &y) {
>>> overflow <C> ret;
>>> if (val > 0 && y.val > 0 && val + y.val < val)
>>> throw std::overflow_error();
>>> /* ... */
>>> ret.val = val + y.val;
>>> return ret;
>>> }
>>> /* ... */
>>> };
>>
>> How is that going to work? The compiler can simply eliminate this line:
>>
>> if (val > 0 && y.val > 0 && val + y.val < val)
>> throw std::overflow_error();
>>
>> because it knows that the guard is always false. I suppose it could be
>> compiled with -fwrapv.
>>
>> Andrew.
>
> Right, as Andrew pointed out, this doesn't work. You are falling into
> the pit of optimization on assumptions of overflow.
>
> The way, which might work, is to do checks on unsigned types. As for
> unsigned-integer-scalar-types overflow is defined.
>
> Kai
>
Technically speaking, casting your signed values to unsigned and using
that to detect overflow will not necessarily work - I think it would be
"implementation defined". But almost all architectures use two's
compliment arithmetic, and it will work there.
I don't know if this is legal C++ (I haven't had enough practice at this
stuff), but the principle would be:
if (val > 0 && y.val > 0 &&
(signed) ((unsigned C) val + (unsigned C) y.val) < val)
throw std::overflow_error();
You also need:
if (val < 0 && y.val < 0 &&
(signed) ((unsigned C) val + (unsigned C) y.val) > val)
throw std::overflow_error();