C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Signed Overflow

From: Julien Villemure-Fréchette <julien.villemure_at_[hidden]>
Date: Thu, 04 Sep 2025 12:58:24 -0400
> Why? Because it's the choice that doesn't result in information loss when overflow occurs.

AFAIK, arithmetic overflow is exactly means that bits of information were lost during the calculation. Furthermore, for the wrap around behavior, the lost bits are the most significant ones, so the result of a wrapping signed overflow on addition or subtraction couldn't be more erroneous than any other possible behavior: the distance from the mathematical result is exactly 2^N. For multiplication, wrapping signed overflow could happen at any intermediate addition so the distance could go up to somewhere near 2^(2N-1). The best possible way to minimize the distance between the mathematical and the result of computation would be to drop the low order bits instead of the high order bits, which is what saturation arithmetic does.


> In particular, for example, in an expression of the form `a + b - c`, if `a + b` overflows but the final result fits in the type, you will actually get the correct result if the type is specified to wrap.

That is not true in the general case, here you assumed that "a>0, b>0, c>=0". What about the case when "c<=0" or "a<0, b<0, c<=0"? Also, if the compiler can deduce that a, b, c are non negative and that "a+b" does not overflow because it is UB, then it can transform "(a+b)-c" into "a+(b-c)" thereby giving a permissible defined behavior; note that this transformation would also be permissible if the overflow behavior is defined to wrap but this wouldn't optimize anything, although if this would change behavior if both trapping and wrapping were required (gcc doesn't allow both, but in principle it could).

In general, for a signed integer expression "(a ± b) ± c" if "a ± b" did overflow then the result of the hole expression may be off by 2^N or 2^(N+1) if a second overflow occurs. If you keep on chaining additions or subtractions then the distance from the true result will be in the set {0, 2^N, 2^(N+1), 2^(N+2), ... 2^(N+M)} adding one possible erroneous value for each operation performed, giving you a low probably of yielding the mathematical result. I'm not even gonna try to count the number erroneous possibilities if you put in the multiplication and division operations, but it is clear that the probability space explodes in numbers.

On September 4, 2025 9:46:56 a.m. EDT, Brian Bi via Std-Proposals <std-proposals_at_[hidden]> wrote:
>On Thu, Sep 4, 2025 at 7:35 AM David Brown via Std-Proposals <
>std-proposals_at_[hidden]> wrote:
>
>> On 04/09/2025 07:48, Peter C++ via Std-Proposals wrote:
>> > FWIW
>> >
>> > one can implement efficient non-UB integer replacement types, eg, for
>> > safety critical systems using enum class types as representation.
>> >
>> > see for example: https://github.com/PeterSommerlad/PSsimplesafeint
>> > <https://github.com/PeterSommerlad/PSsimplesafeint>
>> >
>> > Regards
>> > Peter
>> >
>>
>> You can indeed make such classes - and give them dangerously misleading
>> names like "Simple Safe Int".
>>
>> It is fine to implement wrapping signed integer classes if that's what
>> you want - it is /not/ fine to pretend they are somehow safe or suitable
>> for safety-critical systems. It doesn't matter if your overflows wrap -
>> if you have an overflow, your safety-critical system is screwed.
>>
>> (Some other aspects of your library /are/ positive for safety, such as
>> disallowing mixed signed operations and limiting certain other
>> operations. It's only the overflow behaviour I am in strong
>> disagreement about.)
>>
>>
>> You'd be a lot better off with :
>>
>> using SignificantlySaferIntType = int64_t;
>>
>> That would eliminate the vast majority of signed integer overflow bugs,
>> and give the /correct/ answer to 32-bit overflows.
>>
>> And anyone writing new code with enough care about overflow and code
>> correctness that they might consider something beyond just using "int",
>> will already be able to handle overflow in various ways - such as
>> sanitizing values before calculations ("look before you leap", rather
>> than trying to find a way to recover from jumping off a cliff and
>> somehow ending up miles in the air). The checked arithmetic functions
>> in C++26 are another option.
>>
>>
>>
>>
>> I am in favour of integer class libraries that handle overflow in
>> different ways. But the naming should be based on what is actually done
>> - undefined behaviour, wrapping, zeroing, throwing, trapping, calling
>> terminate/abort/whatever, saturating, returning an unspecified but valid
>> value, setting errno, etc.
>>
>>
>> I am not in favour of pretending or implying that the use of a
>> particular integer class makes the code "safe" (or even just "safer").
>>
>
>I think that actually, wrapping behaviour is safe*r* than UB, saturating
>behaviour, or producing an unspecified value. I won't claim that it's
>completely safe.
>
>Why? Because it's the choice that doesn't result in information loss when
>overflow occurs. In particular, for example, in an expression of the form
>`a + b - c`, if `a + b` overflows but the final result fits in the type,
>you will actually get the correct result if the type is specified to wrap.
>
>
>>
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
>
>
>--
>*Brian Bi*

Received on 2025-09-04 16:58:35