Date: Thu, 4 Sep 2025 13:20:24 -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. [...]
>
What Brian means is that the "mod 2^N" operation distributes over addition,
distributes over subtraction, and distributes over multiplication.
Whether you do the "mod 2^N" operation immediately after each binary
operator, or only once at the end of the whole expression, you'll get the
same answer.
So in this sense, doing a lot of extra "mod 2^N" operations as part of each
binary operator is "value-preserving" because it doesn't interfere with the
value you get at the end.
(((a + b) \mod 2^{32}) - c) \signedmod 2^{32} = (a + b - c)
\signedmod 2^{32}
(((a \times b) \mod 2^{32}) + c) \signedmod 2^{32} = (a \times b + c)
\signedmod 2^{32}
But this nice property doesn't extend to division.
Thus ARM, for example, can get away with a single 32x32=32 multiplication
instruction (`MUL`) but requires two distinct 32x32=32 division
instructions (`SDIV` and `UDIV`).
https://developer.arm.com/documentation/ddi0597/2025-06/Base-Instructions/
Now, philosophically I'd disagree that this property makes wrapping
arithmetic any "safer" than, say, saturating arithmetic, or reliably
aborting the program on overflow, or generating a C++ exception on
overflow. Wrapping arithmetic might even create a moral hazard, by *usually
but not always* giving the right answer. But wrapping arithmetic is
certainly convenient and performant at the *physical* level, yeah, and it
makes perfect sense that an industry codebase might just set `-fwrapv` in
their project file and happily forget all about integer-overflow-is-UB for
years at a time. (I am not aware of any industry codebases that have done
that, but I'm *sure* it has been done *somewhere*.)
–Arthur
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. [...]
>
What Brian means is that the "mod 2^N" operation distributes over addition,
distributes over subtraction, and distributes over multiplication.
Whether you do the "mod 2^N" operation immediately after each binary
operator, or only once at the end of the whole expression, you'll get the
same answer.
So in this sense, doing a lot of extra "mod 2^N" operations as part of each
binary operator is "value-preserving" because it doesn't interfere with the
value you get at the end.
(((a + b) \mod 2^{32}) - c) \signedmod 2^{32} = (a + b - c)
\signedmod 2^{32}
(((a \times b) \mod 2^{32}) + c) \signedmod 2^{32} = (a \times b + c)
\signedmod 2^{32}
But this nice property doesn't extend to division.
Thus ARM, for example, can get away with a single 32x32=32 multiplication
instruction (`MUL`) but requires two distinct 32x32=32 division
instructions (`SDIV` and `UDIV`).
https://developer.arm.com/documentation/ddi0597/2025-06/Base-Instructions/
Now, philosophically I'd disagree that this property makes wrapping
arithmetic any "safer" than, say, saturating arithmetic, or reliably
aborting the program on overflow, or generating a C++ exception on
overflow. Wrapping arithmetic might even create a moral hazard, by *usually
but not always* giving the right answer. But wrapping arithmetic is
certainly convenient and performant at the *physical* level, yeah, and it
makes perfect sense that an industry codebase might just set `-fwrapv` in
their project file and happily forget all about integer-overflow-is-UB for
years at a time. (I am not aware of any industry codebases that have done
that, but I'm *sure* it has been done *somewhere*.)
–Arthur
Received on 2025-09-04 17:20:41