On 04/09/2025 15:46, Brian Bi wrote:
>
>
> 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.
Of course it results in information loss on overflow. The only way to
avoid information loss is to have big enough types so that you don't get
an overflow in the first place.
Let's pretend we have 4 bit ints - because it makes the numbers much
smaller and easier.
If you write "a + b" with wrapping, and get a result of -2, how do you
know the result is correct? How do you know the correct mathematical
sum of "a" and "b" is -2, and not 14 ? You don't - you have lost
information when overflow occurred. You do not get the right answer for
integer summation.
> 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.
>
Sometimes that is true (such as for simple additions and subtractions),
sometimes it is not - depending on the expression you are using.
If you write "x * y / z" and "x * y" overflows, you will get the wrong
result with wrapping. But you might get the correct result with UB
overflow if "y" and "z" are known at compile time and have a large
enough common factor to reduce the values and avoid the overflow.
All you are doing here is saying that with wrapping, you will sometimes
get the desired end result despite incorrect code with intermediary
errors. That is not particularly helpful (it is, to borrow a phrase
from another poster, "Voodoo programming").
And it is no different from UB on overflow, which - being UB - will
sometimes give you the results you wanted.
What is the justification for saying that the code `a + b - c` where the intermediate result overflows, but the final result fits in the type, is incorrect code?
It is incorrect because it has UB.
We could make it not UB. Would it still be incorrect then? Maybe, but you would need to come up with a new argument as to why it's incorrect.
I think that it can be made correct in those cases where the programmer knows that the final result fits in the type. There is only one possible result that the programmer could have intended. We can make it correct by making it not UB.
In the multiplication case we cannot make it correct. The problem is that the programmer's intent is not clear. They could have wanted the mathematically correct result or they could have wanted the result that they would get from wrapping behaviour.
--