C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Signed Overflow

From: Brian Bi <bbi5291_at_[hidden]>
Date: Thu, 4 Sep 2025 13:16:19 -0400
On Thu, Sep 4, 2025 at 12:58 PM Julien Villemure-Fréchette via
Std-Proposals <std-proposals_at_[hidden]> wrote:

> > 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.
>
>
There are specialized use cases for saturating arithmetic. I'm surprised to
see it being repeatedly brought up as a plausible *general* behaviour for
integer overflow.

I keep hearing people bring up the freedom that UB gives to the
implementation. Is anyone aware of any implementation that has used this
freedom to implement saturating behaviour in order to minimize the error?

Is anyone aware of any popular programming language that defines the
behaviour of the built-in arithmetic operations on the built-in integer
types to saturate on overflow, or even provides an option to configure the
behaviour as such?

What conclusions can we draw? Saturating arithmetic has its use cases, but
outside of those use cases the expectation when using integer arithmetic is
that the mathematically correct result will be produced. If that result
cannot be produced, the code has a bug.


>
> > 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).
>

I did not assume anything about the signs of `a`, `b`, and `c`. I said that
*if* the final result fits in the type, then wrapping behaviour will give
the right answer after the intermediate overflow. No other semantics can
guarantee the correct result in that case (UB of course allows it but does
not guarantee it).


>
> 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
>>>
>>
>> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>


-- 
*Brian Bi*

Received on 2025-09-04 17:16:36