Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

In the end, rason will come

109 views
Skip to first unread message

Bonita Montero

unread,
Jul 31, 2019, 4:14:17 AM7/31/19
to

Chris M. Thomasson

unread,
Jul 31, 2019, 4:25:30 AM7/31/19
to
On 7/31/2019 1:14 AM, Bonita Montero wrote:
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r0.html
>

The One True Representation!

Öö Tiib

unread,
Jul 31, 2019, 6:48:32 AM7/31/19
to
On Wednesday, 31 July 2019 11:14:17 UTC+3, Bonita Montero wrote:
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r0.html

This feels like two major proposals. One is that all signed integers
should be two's complement and other is that signed integer overflow
should wrap.

Majority will be happy (or at least content) with both but about
second there will be likely a bit of opposition and controversy.

Most of such opposition will boil down to claim that signed
arithmetic should not overflow anyway in sane code and therefore
some optimizations where compiler did assume that it does not
overflow will be lost without any gain.

I agree that that signed arithmetic should not overflow
in good code but it seems hard to find (or to teach) programmers
who write good code in that respect. So there are often bugs with
it and so I would often prefer crash (or have SIGFPE raised)
instead of wrapping.

James Kuyper

unread,
Jul 31, 2019, 7:58:39 AM7/31/19
to
That document claims:
> It is extremely unlikely that there exist any significant code base> developed for two’s complement machines that
> would actually work when run on a non-two’s
> complement machine.

I consider that implausible, unless "developed for two's complement
machines" is defined as "developed with the deliberate intent of relying
on two's complement", in which case it becomes a trivial tautology.

Most of my code is intended to work regardless of how signs are
represented, and the discipline required to achieve that result is not
great: don't apply bit-wise operators (<< >> | & ~) to values of signed
types. Don't use type punning. Whenever doing calculations involving
signed integer types, make sure that the specific types chosen are big
enough to avoid overflow - and when a calculation cannot be guaranteed
to avoid overflow for all permitted values of its inputs, make sure the
calculation is not performed when the values are such that overflow
would occur.

I'm sure that there have to be at a least a few significant code bases
where such discipline has been followed - though I cannot identify any
specific ones.

Siri Cruise

unread,
Jul 31, 2019, 8:14:41 AM7/31/19
to
In article <qhrvp4$i1c$1...@dont-email.me>,
James Kuyper <james...@alumni.caltech.edu> wrote:

> On 7/31/19 4:25 AM, Chris M. Thomasson wrote:
> > On 7/31/2019 1:14 AM, Bonita Montero wrote:
> >> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r0.html
> >>
> >
> > The One True Representation!
>
> That document claims:
> > It is extremely unlikely that there exist any significant code base>
> > developed for two’s complement machines that
> > would actually work when run on a non-two’s
> > complement machine.
>
> I consider that implausible, unless "developed for two's complement
> machines" is defined as "developed with the deliberate intent of relying
> on two's complement", in which case it becomes a trivial tautology.

I programmed on CDC ones complement machines and CDC twos complement machines. I
never had a problem except -0.

> great: don't apply bit-wise operators (<< >> | & ~) to values of signed
> types. Don't use type punning. Whenever doing calculations involving
> signed integer types, make sure that the specific types chosen are big

It's difficult to run into differences. Even bit twiddling code on signed
numbers can work fairly transparently. -0 can be a problem, but only in
assembly. Compilers don't let you say -0 < +0.

--
:-<> Siri Seal of Disavowal #000-001. Disavowed. Denied. Deleted. @
'I desire mercy, not sacrifice.' /|\
The first law of discordiamism: The more energy This post / \
to make order is nore energy made into entropy. insults Islam. Mohammed

Alf P. Steinbach

unread,
Jul 31, 2019, 8:59:57 AM7/31/19
to
As I recall from a novel I read, One True is a mean-spirited artifical
intelligence that takes over Earth and eventually Mars, with more or
less direct control over each individual human. It can convert any human
to an obedient slave by just saying a few words to the person. So it's
best not to create any opportunity where one might hear what it says.

Cheers!,

- Alf

David Brown

unread,
Jul 31, 2019, 9:30:21 AM7/31/19
to
On 31/07/2019 12:48, Öö Tiib wrote:
> On Wednesday, 31 July 2019 11:14:17 UTC+3, Bonita Montero wrote:
>> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r0.html
>
> This feels like two major proposals. One is that all signed integers
> should be two's complement and other is that signed integer overflow
> should wrap.
>
> Majority will be happy (or at least content) with both but about
> second there will be likely a bit of opposition and controversy.

I doubt if anyone will mind about the first. Representation is
implementation specific, but since I think it is fair to say that any
system which will support modern C++ (or modern C) will have two's
complement representation, this is pretty much a no-brainer. It will
simplify several points in the standard and make some kinds of coding
simpler, while having no adverse effects that I can think of.


There will, I think, be quite a bit of opposition to the second. I am
certainly against it, for several reasons.

Some things that are currently implementation defined, such as the
conversion to signed types when the value is out of range, could
usefully and sensibly be defined as two's complement wrapping or modulo
arithmetic. That is what happens today in most (or all) compilers, and
would be a natural choice to stipulate in the standards along with the
representation.

>
> Most of such opposition will boil down to claim that signed
> arithmetic should not overflow anyway in sane code and therefore
> some optimizations where compiler did assume that it does not
> overflow will be lost without any gain.
>
> I agree that that signed arithmetic should not overflow
> in good code but it seems hard to find (or to teach) programmers
> who write good code in that respect. So there are often bugs with
> it and so I would often prefer crash (or have SIGFPE raised)
> instead of wrapping.
>

I see a number of reasons why two's complement wrapping for signed
arithmetic is a bad idea.

1. It hinders optimisations. When you have division involved, you lose
several associative properties of integer arithmetic. And you lose all
sorts of basic identities that exist when you assume no overflow, such
as "x > y <=> (x + z) > (y + z)".

2. It makes alternative overflow behaviour wrong and non-conforming.
That means a compiler that has traps on overflows for safer coding or
during debugging, is suddenly no longer correct C++.

3. It gives broken and incorrect code a defined behaviour, making it
harder for compilers and static analysis tools to inform the programmer
and help find mistakes. Instead of being able to tell you that you've
overflowed your integer constant (or constexpr) calculations, you'd have
to put the compiler in non-conforming modes first.

4. Integer arithmetic that overflows is almost always in error - even if
the results are defined (by the language, compiler flags, etc.). If you
have a pile of 2147483647 apples and put another apple on the pile, a
result of -2147483648 is about the silliest answer you could possibly
give. Raising an error or saturating would be far more likely to be
correct.

5. It gives the impression that signed integer arithmetic is now "safe"
and can be carried out without as much care and attention as you had to
do when there were nasal demons lurking round the bend. This is, of
course, nonsense - it merely hides some classes of errors under the
carpet where they are harder to find.


So I am against defining the results of signed integer arithmetic
primarily for code safety and correctness reasons, and secondarily for
performance reasons.

Keith Thompson

unread,
Jul 31, 2019, 4:05:52 PM7/31/19
to
Öö Tiib <oot...@hot.ee> writes:
> On Wednesday, 31 July 2019 11:14:17 UTC+3, Bonita Montero wrote:
>> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r0.html

The link is to:
p0907r0
Signed Integers are Two’s Complement
Published Proposal, 9 February 2018

> This feels like two major proposals. One is that all signed integers
> should be two's complement and other is that signed integer overflow
> should wrap.
>
> Majority will be happy (or at least content) with both but about
> second there will be likely a bit of opposition and controversy.
>
> Most of such opposition will boil down to claim that signed
> arithmetic should not overflow anyway in sane code and therefore
> some optimizations where compiler did assume that it does not
> overflow will be lost without any gain.
>
> I agree that that signed arithmetic should not overflow
> in good code but it seems hard to find (or to teach) programmers
> who write good code in that respect. So there are often bugs with
> it and so I would often prefer crash (or have SIGFPE raised)
> instead of wrapping.

I have no particular objection to requiring two's complement for signed
integers. I don't think this proposal addresses padding bits or the
possibility that the most negative representation is a trap
representation, so would still be some rare variations to worry about.

Specifying wrapping behavior for signed integers could be a *big*
problem. In particular, this:

int n = INT_MAX + 1;

would probably not produce a warning. It's not required to do
so now, and compilers could produce warnings anyway, but making
INT_MAX + 1 well defined where it's currently almost certainly an
error would be confusing. More concretely, this:

constexpr int n = INT_MAX + 1;

is currently invalid and would become legal.

Requiring 2's-complement representation while leaving overflow
behavior alone would be a simpler and less controversial change.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

Öö Tiib

unread,
Jul 31, 2019, 5:23:01 PM7/31/19
to
On Wednesday, 31 July 2019 16:30:21 UTC+3, David Brown wrote:
> On 31/07/2019 12:48, Öö Tiib wrote:
> >
> > I agree that that signed arithmetic should not overflow
> > in good code but it seems hard to find (or to teach) programmers
> > who write good code in that respect. So there are often bugs with
> > it and so I would often prefer crash (or have SIGFPE raised)
> > instead of wrapping.
> >
>
> I see a number of reasons why two's complement wrapping for signed
> arithmetic is a bad idea.

I was almost certain that you are one who is against it considering
what you have written before.

>
> 1. It hinders optimisations. When you have division involved, you lose
> several associative properties of integer arithmetic. And you lose all
> sorts of basic identities that exist when you assume no overflow, such
> as "x > y <=> (x + z) > (y + z)".

That is true, (like I said), there are several optimizations that
assume that integer arithmetic does not overflow and it will be
tricky to indicate to compiler that it may do such optimizations
in conforming mode. But so ... solution is same as with your #2.

> 2. It makes alternative overflow behaviour wrong and non-conforming.
> That means a compiler that has traps on overflows for safer coding or
> during debugging, is suddenly no longer correct C++.

The compiler vendors have never cared that with certain options
their compiler is not conforming. Quite popular is to let to turn
off exceptions, to turn off RTTI and so on. I do not think that turning
off wrapping signed integers will be different in any way.

> 3. It gives broken and incorrect code a defined behaviour, making it
> harder for compilers and static analysis tools to inform the programmer
> and help find mistakes. Instead of being able to tell you that you've
> overflowed your integer constant (or constexpr) calculations, you'd have
> to put the compiler in non-conforming modes first.

Please elaborate. There are piles of warnings that a well-formed
program with fully well-defined behavior will receive with -Wall
and most warnings in -Wextra are such plus there are more warnings
I like that neither of those settings turns on. No non-conforming
modes are needed. So why should warning about potentially wrapping
integer suddenly cause different situation?

> 4. Integer arithmetic that overflows is almost always in error - even if
> the results are defined (by the language, compiler flags, etc.). If you
> have a pile of 2147483647 apples and put another apple on the pile, a
> result of -2147483648 is about the silliest answer you could possibly
> give. Raising an error or saturating would be far more likely to be
> correct.

That option I would also love like I said above.

> 5. It gives the impression that signed integer arithmetic is now "safe"
> and can be carried out without as much care and attention as you had to
> do when there were nasal demons lurking round the bend. This is, of
> course, nonsense - it merely hides some classes of errors under the
> carpet where they are harder to find.

It is frequently same with unsigned integers. On lot of cases when
these overflow then it is defect.

> So I am against defining the results of signed integer arithmetic
> primarily for code safety and correctness reasons, and secondarily for
> performance reasons.

But you put performance reason as #1 in above list. ;)

Jorgen Grahn

unread,
Jul 31, 2019, 6:35:20 PM7/31/19
to
On Wed, 2019-07-31, James Kuyper wrote:
> On 7/31/19 4:25 AM, Chris M. Thomasson wrote:
>> On 7/31/2019 1:14 AM, Bonita Montero wrote:
>>> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r0.html
>>>
>>
>> The One True Representation!
>
> That document claims:

>> It is extremely unlikely that there exist any significant code base
>> developed for two's complement machines that
>> would actually work when run on a non-two's
>> complement machine.
>
> I consider that implausible, unless "developed for two's complement
> machines" is defined as "developed with the deliberate intent of relying
> on two's complement", in which case it becomes a trivial tautology.

Yes. A really weird thing to write, now that you point it out.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Öö Tiib

unread,
Jul 31, 2019, 6:48:42 PM7/31/19
to
On Wednesday, 31 July 2019 23:05:52 UTC+3, Keith Thompson wrote:
> Specifying wrapping behavior for signed integers could be a *big*
> problem. In particular, this:
>
> int n = INT_MAX + 1;
>
> would probably not produce a warning. It's not required to do
> so now, and compilers could produce warnings anyway, but making
> INT_MAX + 1 well defined where it's currently almost certainly an
> error would be confusing. More concretely, this:
>
> constexpr int n = INT_MAX + 1;
>
> is currently invalid and would become legal.

I feel confused, either I misunderstand the point you are making
or have misread standard.
Right now (as with C++17) the std::numeric_limits<int>::is_modulo
has implementation-defined constexpr value. Yes?
That value can depend on compiler options if it is true
or false. Yes?
My reading of standard is that when it is true then INT_MAX + 1
is not undefined behavior. Do I misread?
Are you saying that it should be undefined behavior?
I read that the proposal was to make it true always and that I'm
also unsure if it is good idea.

Keith Thompson

unread,
Jul 31, 2019, 7:32:14 PM7/31/19
to
As far as I know, that's all correct.

I hadn't known about is_modulo.

For g++ and clang++, is_modulo is false for signed integer types, which
makes the behavior of INT_MAX + 1 undefined. I don't know whether
there's an option that makes it true, or what other implementations do.

Yes, that does affect the issue. The proposal would effectively require
is_modulo to be true for signed types.

It would be nice to have a way for a programmer, not just an
implementation, to decide whether a given signed type has wraparound or
overflow semantics. That's probably beyond the scope of this proposal.

Öö Tiib

unread,
Jul 31, 2019, 10:21:48 PM7/31/19
to
About g++ I remember that it was true. However it did
behave in non-conforming and inconsistent manner. There was
perhaps too lot of work to fix it so it was put to false under
every set of options at 2012. That Bugzilla issue was about it:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=22200
Feels hostile discussion like as people had had some long and
nauseous flame wars in lists. About more recent developments or
other compilers I don't also know.

> Yes, that does affect the issue. The proposal would effectively require
> is_modulo to be true for signed types.
>
> It would be nice to have a way for a programmer, not just an
> implementation, to decide whether a given signed type has wraparound or
> overflow semantics. That's probably beyond the scope of this proposal.

Hmm ... some want it to be UB, some want it to trap, some want it to
have saturating NaN, some want it to wrap and some want to pick
depending on circumstances.
To please everybody is behind any scope I can imagine. ;)

David Brown

unread,
Aug 1, 2019, 5:26:16 PM8/1/19
to
On 31/07/2019 23:22, Öö Tiib wrote:
> On Wednesday, 31 July 2019 16:30:21 UTC+3, David Brown wrote:
>> On 31/07/2019 12:48, Öö Tiib wrote:
>>>
>>> I agree that that signed arithmetic should not overflow
>>> in good code but it seems hard to find (or to teach) programmers
>>> who write good code in that respect. So there are often bugs with
>>> it and so I would often prefer crash (or have SIGFPE raised)
>>> instead of wrapping.
>>>
>>
>> I see a number of reasons why two's complement wrapping for signed
>> arithmetic is a bad idea.
>
> I was almost certain that you are one who is against it considering
> what you have written before.
>
>>
>> 1. It hinders optimisations. When you have division involved, you lose
>> several associative properties of integer arithmetic. And you lose all
>> sorts of basic identities that exist when you assume no overflow, such
>> as "x > y <=> (x + z) > (y + z)".
>
> That is true, (like I said), there are several optimizations that
> assume that integer arithmetic does not overflow and it will be
> tricky to indicate to compiler that it may do such optimizations
> in conforming mode.

Such optimisations won't be tricky - they will be completely impossible.

> But so ... solution is same as with your #2.
>

Of course. Any defined behaviour on overflow restricts optimisations.
It's good to be able to choose what you want in different circumstances
- if you want traps on overflow to aid debugging, you don't mind the
cost in optimisations.

>> 2. It makes alternative overflow behaviour wrong and non-conforming.
>> That means a compiler that has traps on overflows for safer coding or
>> during debugging, is suddenly no longer correct C++.
>
> The compiler vendors have never cared that with certain options
> their compiler is not conforming. Quite popular is to let to turn
> off exceptions, to turn off RTTI and so on. I do not think that turning
> off wrapping signed integers will be different in any way.
>

It will be a completely different thing. And I think that is so obvious
it doesn't need more explanation.

>> 3. It gives broken and incorrect code a defined behaviour, making it
>> harder for compilers and static analysis tools to inform the programmer
>> and help find mistakes. Instead of being able to tell you that you've
>> overflowed your integer constant (or constexpr) calculations, you'd have
>> to put the compiler in non-conforming modes first.
>
> Please elaborate. There are piles of warnings that a well-formed
> program with fully well-defined behavior will receive with -Wall
> and most warnings in -Wextra are such plus there are more warnings
> I like that neither of those settings turns on. No non-conforming
> modes are needed. So why should warning about potentially wrapping
> integer suddenly cause different situation?
>

It is conceivable that compilers will have warnings for overflows on
signed arithmetic, even when it is fully defined - but I think it is
unlikely except with a view to compatibility for current standards.
Compilers don't warn when unsigned arithmetic overflows, because it is
fully defined.

The difference is that when someone writes, for example, "if (a = 1)",
they have almost certainly made a mistake. So it is not unreasonable
for compilers to warn about it (and not unreasonable that it requires a
non-default flag to get the warning, since it is valid C or C++ code).
But if and when signed integer overflow becomes fully defined with
wrapping behaviour, it is no longer a mistake. How is the compiler
supposed to guess what was intentional behaviour and what was likely to
be a programmer error?

>> 4. Integer arithmetic that overflows is almost always in error - even if
>> the results are defined (by the language, compiler flags, etc.). If you
>> have a pile of 2147483647 apples and put another apple on the pile, a
>> result of -2147483648 is about the silliest answer you could possibly
>> give. Raising an error or saturating would be far more likely to be
>> correct.
>
> That option I would also love like I said above.
>

That cannot be an option if wrapping is the defined behaviour.

It is fine to have flags that add semantics to the language in place of
undefined behaviour. It is not fine to have flags that give completely
different semantics to something the language defines. This is not like
enabling or disabling particular features, such as exceptions or RTTI
(features which are typically only disabled for niche uses, such as
resource limited embedded systems).

>> 5. It gives the impression that signed integer arithmetic is now "safe"
>> and can be carried out without as much care and attention as you had to
>> do when there were nasal demons lurking round the bend. This is, of
>> course, nonsense - it merely hides some classes of errors under the
>> carpet where they are harder to find.
>
> It is frequently same with unsigned integers. On lot of cases when
> these overflow then it is defect.
>

Agreed. I would be happy for unsigned arithmetic to be undefined on
overflow in most cases. However, there are occasional situations where
wrapping behaviour is clearly useful and correct, so it is important
that there is /some/ way to get it. If unsigned arithmetic were not
defined with wrapping in C and C++, there would have to be another way
to do it for these occasional uses.

>> So I am against defining the results of signed integer arithmetic
>> primarily for code safety and correctness reasons, and secondarily for
>> performance reasons.
>
> But you put performance reason as #1 in above list. ;)
>

It was not ordered by importance (or what I feel is most important).
Correctness always trumps efficiency, and aids to correctness are
therefore high on my list of important features.

Öö Tiib

unread,
Aug 1, 2019, 10:53:31 PM8/1/19
to
On Friday, 2 August 2019 00:26:16 UTC+3, David Brown wrote:
> On 31/07/2019 23:22, Öö Tiib wrote:
> > On Wednesday, 31 July 2019 16:30:21 UTC+3, David Brown wrote:
> >> On 31/07/2019 12:48, Öö Tiib wrote:
> >>>
> >>> I agree that that signed arithmetic should not overflow
> >>> in good code but it seems hard to find (or to teach) programmers
> >>> who write good code in that respect. So there are often bugs with
> >>> it and so I would often prefer crash (or have SIGFPE raised)
> >>> instead of wrapping.
> >>>
> >>
> >> I see a number of reasons why two's complement wrapping for signed
> >> arithmetic is a bad idea.
> >
> > I was almost certain that you are one who is against it considering
> > what you have written before.
> >
> >>
> >> 1. It hinders optimisations. When you have division involved, you lose
> >> several associative properties of integer arithmetic. And you lose all
> >> sorts of basic identities that exist when you assume no overflow, such
> >> as "x > y <=> (x + z) > (y + z)".
> >
> > That is true, (like I said), there are several optimizations that
> > assume that integer arithmetic does not overflow and it will be
> > tricky to indicate to compiler that it may do such optimizations
> > in conforming mode.
>
> Such optimisations won't be tricky - they will be completely impossible.

Why? Making compiler to warn that "(x + z) > (y + z)" is maybe sub-optimal
code and to have some attribute or pragma for to suppress it when wrap
*was* reason to write it like that is not impossible just tricky.
Other likely outcome is that iterator or range based loops will simply
be tiny bit more efficient than that int i as index.
That can be even good. In Rust it is so and all are happy.

> > But so ... solution is same as with your #2.
>
>
> Of course. Any defined behaviour on overflow restricts optimisations.
> It's good to be able to choose what you want in different circumstances
> - if you want traps on overflow to aid debugging, you don't mind the
> cost in optimisations.
>
> >> 2. It makes alternative overflow behaviour wrong and non-conforming.
> >> That means a compiler that has traps on overflows for safer coding or
> >> during debugging, is suddenly no longer correct C++.
> >
> > The compiler vendors have never cared that with certain options
> > their compiler is not conforming. Quite popular is to let to turn
> > off exceptions, to turn off RTTI and so on. I do not think that turning
> > off wrapping signed integers will be different in any way.
> >
>
> It will be a completely different thing. And I think that is so obvious
> it doesn't need more explanation.

Ok. :/

>
> >> 3. It gives broken and incorrect code a defined behaviour, making it
> >> harder for compilers and static analysis tools to inform the programmer
> >> and help find mistakes. Instead of being able to tell you that you've
> >> overflowed your integer constant (or constexpr) calculations, you'd have
> >> to put the compiler in non-conforming modes first.
> >
> > Please elaborate. There are piles of warnings that a well-formed
> > program with fully well-defined behavior will receive with -Wall
> > and most warnings in -Wextra are such plus there are more warnings
> > I like that neither of those settings turns on. No non-conforming
> > modes are needed. So why should warning about potentially wrapping
> > integer suddenly cause different situation?
> >
>
> It is conceivable that compilers will have warnings for overflows on
> signed arithmetic, even when it is fully defined - but I think it is
> unlikely except with a view to compatibility for current standards.
> Compilers don't warn when unsigned arithmetic overflows, because it is
> fully defined.
>
> The difference is that when someone writes, for example, "if (a = 1)",
> they have almost certainly made a mistake. So it is not unreasonable
> for compilers to warn about it (and not unreasonable that it requires a
> non-default flag to get the warning, since it is valid C or C++ code).
> But if and when signed integer overflow becomes fully defined with
> wrapping behaviour, it is no longer a mistake. How is the compiler
> supposed to guess what was intentional behaviour and what was likely to
> be a programmer error?

I don't know who does not agree that signed integer overflow is likely
a bug and worth warning about. If someone writes some code that uses
deliberately wrapping (for example retroactively to test if overflow
did happen) then they can suppress that warning locally with some
pragma or attribute.


> >> 4. Integer arithmetic that overflows is almost always in error - even if
> >> the results are defined (by the language, compiler flags, etc.). If you
> >> have a pile of 2147483647 apples and put another apple on the pile, a
> >> result of -2147483648 is about the silliest answer you could possibly
> >> give. Raising an error or saturating would be far more likely to be
> >> correct.
> >
> > That option I would also love like I said above.
> >
>
> That cannot be an option if wrapping is the defined behaviour.

Note that both g++ and clang have -fwrapv and -ftrapv just neither
of those work reliably.

Requiring at least working -fwrapv would be bit better
than nothing. I could bit cheaper (or at least bit more readably) write
my own throwing or saturating integers using wrapping integers.
I like trapping best but I understand that it is most expensive on
majority of platforms.

> It is fine to have flags that add semantics to the language in place of
> undefined behaviour. It is not fine to have flags that give completely
> different semantics to something the language defines. This is not like
> enabling or disabling particular features, such as exceptions or RTTI
> (features which are typically only disabled for niche uses, such as
> resource limited embedded systems).

Yes, you have good point here that switching between different well
defined behaviors is evil.
It is maybe niche ... but it is niche that we *own* right now.
I have about third of my career (plus sometimes as hobby) participated
in such projects. There are literally tons of electronics made all
around and other languages but C and C++ are only sometimes
experimentally tried there. Also the optimizations often matter
only on such limited systems somewhat.


> >> 5. It gives the impression that signed integer arithmetic is now "safe"
> >> and can be carried out without as much care and attention as you had to
> >> do when there were nasal demons lurking round the bend. This is, of
> >> course, nonsense - it merely hides some classes of errors under the
> >> carpet where they are harder to find.
> >
> > It is frequently same with unsigned integers. On lot of cases when
> > these overflow then it is defect.
> >
>
> Agreed. I would be happy for unsigned arithmetic to be undefined on
> overflow in most cases. However, there are occasional situations where
> wrapping behaviour is clearly useful and correct, so it is important
> that there is /some/ way to get it. If unsigned arithmetic were not
> defined with wrapping in C and C++, there would have to be another way
> to do it for these occasional uses.

Yes, some of the code to avoid that undefined behavior with signed
integers uses unsigned right now (and assumes twos complement).
The well-defined behavior can not make wrong code to give
correct answers. The wrong answers will just come more consistently
and portably with wrap and so I can trust unit tests for my
math of embedded system ran on PC bit more.

> >> So I am against defining the results of signed integer arithmetic
> >> primarily for code safety and correctness reasons, and secondarily for
> >> performance reasons.
> >
> > But you put performance reason as #1 in above list. ;)
> >
>
> It was not ordered by importance (or what I feel is most important).
> Correctness always trumps efficiency, and aids to correctness are
> therefore high on my list of important features.

I have same views especially about arithmetic that is fast. For
massive amounts of algebra it is better to use libraries that
utilize GPU or the like anyway. I still do not understand how
undefined behavior is supposed to be more safe and reliable than
defined behavior ... but perhaps it is just me.

Christian Gollwitzer

unread,
Aug 2, 2019, 1:41:40 AM8/2/19
to
Am 01.08.19 um 23:25 schrieb David Brown:
> It was not ordered by importance (or what I feel is most important).
> Correctness always trumps efficiency, and aids to correctness are
> therefore high on my list of important features.

Then the right thing to do would be mathematical signed integers, which
can not overflow at all (like those in Python, short of memory
exhaustion) for the type "int". Wrapping integers for int8_t, uint8_t
etc. No general unsigned int, unless it throws an exception on negative
numbers.

The only reason no one wants to suggest this, is performance.

Christian

Bonita Montero

unread,
Aug 2, 2019, 2:10:48 AM8/2/19
to
> Specifying wrapping behavior for signed integers could be a *big*
> problem. In particular, this:
> int n = INT_MAX + 1;

That's not the least problem because no one writes such code.

David Brown

unread,
Aug 2, 2019, 2:46:30 PM8/2/19
to
It is impossible, because the code would no longer be correct (unless
the compiler has other information about the ranges). If one of (x + z)
or (y + z) wraps and the other does not, optimising to "x > y" would not
be valid.

Try this with gcc:

bool foo(int x, int y, int z) {
return (x + z) > (y + z);
}

#pragma GCC optimize "-fwrapv"
bool foo2(int x, int y, int z) {
return (x + z) > (y + z);
}

(from the usual https://godbolt.org>

foo:
cmp edi, esi
setg al
ret
foo2:
add edi, edx
add edx, esi
cmp edi, edx
setg al
ret

When signed integer overflow has two's complement wrapping, you can't
simplify code using as many normal mathematical integer identities. You
can still do some re-arrangements and simplifications - more than if
overflow is defined as trapping - but you lose some case, especially
those involving relations and inequalities.

> Other likely outcome is that iterator or range based loops will simply
> be tiny bit more efficient than that int i as index.
> That can be even good. In Rust it is so and all are happy.
>

When the compiler has more information about the possible ranges of the
numbers involved, it can do more optimisation. That is the case for
iterators.

But it is not a good thing that some types of code become less efficient
- the fact that other types of code might not be affected does not
suddenly make it good.

A change that makes current, correct, working code slower is never a
good thing by itself. It is only worth having if there are significant
advantages. Turning broken code with arbitrary bad behaviour into
broken code with predictable bad behaviour is not particularly useful.

Let people who think that wrapping integers are somehow good or safe use
other languages. There is no need to weaken C++ with the same mistake.
So you think this feature - wrapping overflows - is so useful and
important that it should be added to the language and forced upon all
compilers, and yet it also is so unlikely to be correct code that
compilers should warn about it whenever possible and require specific
settings to disable the warning? Isn't that a little inconsistent?

I'd be much happier to see some standardised pragmas like:

#pragma STDC_OVERFLOW_WRAP
#pragma STDC_OVERFLOW_TRAP
#pragma STDC_OVERFLOW_UNDEFINED

(or whatever variant is preferred)

where undefined behaviour is the standard, but people can choose
specific defined behaviour if they want, using a standardised method.
Basically, allow #pragma GCC optimize "-fwrapv" in a common form.
Surely that would be enough for those that want wrapping behaviour,
without bothering anyone else?


>
>
>>>> 4. Integer arithmetic that overflows is almost always in error - even if
>>>> the results are defined (by the language, compiler flags, etc.). If you
>>>> have a pile of 2147483647 apples and put another apple on the pile, a
>>>> result of -2147483648 is about the silliest answer you could possibly
>>>> give. Raising an error or saturating would be far more likely to be
>>>> correct.
>>>
>>> That option I would also love like I said above.
>>>
>>
>> That cannot be an option if wrapping is the defined behaviour.
>
> Note that both g++ and clang have -fwrapv and -ftrapv just neither
> of those work reliably.
>

"-fwrapv" does exactly what it says on the tin, as far as I know. Do
you know of any problems with it, or any way in which it does not work
reliably? Neither gcc nor clang are bug-free, of course, and given that
this is an option that is used rarely it will not receive as much heavy
testing as other aspects of the tools. But the intention is that this
is a working and maintained option that gives you specific new semantics
in the compiler.

"-ftrapv" has always been a bit limited, and somewhat poorly defined.
It is not clear how much rearrangement is done before the trapping
operations are used, and support varies from target to target. AFAIUI,
you are recommended to use -fsanitize=signed-integer-overflow instead.

> Requiring at least working -fwrapv would be bit better
> than nothing.

Again, what do you think does not work with -fwrapv?

> I could bit cheaper (or at least bit more readably) write
> my own throwing or saturating integers using wrapping integers.

For gcc and clang, you are better off using the overflow arithmetic
builtin functions.

> I like trapping best but I understand that it is most expensive on
> majority of platforms.
>

It is expensive on /all/ platforms, because it disables a good many
re-arrangements and simplifications of expressions. Beyond that, it
usually boils down to adding "trap if overflow" or "branch if overflow"
instructions after arithmetic operations - and the cost of that does
vary between targets.

But it is probably cheaper than saturating arithmetic in many cases,
which also disables many re-arrangements and requires extra code for
targets that don't have saturating arithmetic instructions.

>> It is fine to have flags that add semantics to the language in place of
>> undefined behaviour. It is not fine to have flags that give completely
>> different semantics to something the language defines. This is not like
>> enabling or disabling particular features, such as exceptions or RTTI
>> (features which are typically only disabled for niche uses, such as
>> resource limited embedded systems).
>
> Yes, you have good point here that switching between different well
> defined behaviors is evil.
> It is maybe niche ... but it is niche that we *own* right now.
> I have about third of my career (plus sometimes as hobby) participated
> in such projects. There are literally tons of electronics made all
> around and other languages but C and C++ are only sometimes
> experimentally tried there. Also the optimizations often matter
> only on such limited systems somewhat.
>

My career has been dominated by programming on platforms which are small
enough that you typically disable exceptions and RTTI when using C++.
(Things might change in the future with the newer ideas for cheaper C++
exceptions.) And yes, it is a very important niche, especially for C.

>
>>>> 5. It gives the impression that signed integer arithmetic is now "safe"
>>>> and can be carried out without as much care and attention as you had to
>>>> do when there were nasal demons lurking round the bend. This is, of
>>>> course, nonsense - it merely hides some classes of errors under the
>>>> carpet where they are harder to find.
>>>
>>> It is frequently same with unsigned integers. On lot of cases when
>>> these overflow then it is defect.
>>>
>>
>> Agreed. I would be happy for unsigned arithmetic to be undefined on
>> overflow in most cases. However, there are occasional situations where
>> wrapping behaviour is clearly useful and correct, so it is important
>> that there is /some/ way to get it. If unsigned arithmetic were not
>> defined with wrapping in C and C++, there would have to be another way
>> to do it for these occasional uses.
>
> Yes, some of the code to avoid that undefined behavior with signed
> integers uses unsigned right now (and assumes twos complement).

Indeed - and that is often fine, though perhaps of limited portability.
(Making two's complement representation a requirement will fix this.)

> The well-defined behavior can not make wrong code to give
> correct answers. The wrong answers will just come more consistently
> and portably with wrap and so I can trust unit tests for my
> math of embedded system ran on PC bit more.
>

If your unit tests rely on wrapping for overflow, then those unit tests
are broken. "More consistent wrong answers" is /not/ a phrase you want
to hear regarding test code! You want your embedded code to run quickly
and efficiently - but there is no reason not to have
-fsanitize=signed-integer-overflow for your PC-based unit tests and
simulations.

>>>> So I am against defining the results of signed integer arithmetic
>>>> primarily for code safety and correctness reasons, and secondarily for
>>>> performance reasons.
>>>
>>> But you put performance reason as #1 in above list. ;)
>>>
>>
>> It was not ordered by importance (or what I feel is most important).
>> Correctness always trumps efficiency, and aids to correctness are
>> therefore high on my list of important features.
>
> I have same views especially about arithmetic that is fast. For
> massive amounts of algebra it is better to use libraries that
> utilize GPU or the like anyway. I still do not understand how
> undefined behavior is supposed to be more safe and reliable than
> defined behavior ... but perhaps it is just me.
>

Undefined behaviour is something that your tools know is wrong. That
means that there is at least a chance that the tools can spot mistakes.
They can't do it all the time, and sometimes there are significant costs
in run-time tests for find mistakes, but it is possible. Look at the
sanitize options at
<https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html> (or
the clang page if you prefer), to see some of the mistakes that can be
caught at compile time. They can only be caught because they are
mistakes - they have no defined behaviour.

If something is given defined behaviour by the language, then the
compiler must assume that when it occurs, it is intentional. At best,
any compile-time or run-time checking for this must be an optional
feature with a high risk of false positives.

Additionally, adding this new semantics to the language would likely
confuse people and make them think it was always the case. There are
far too many programmers already who think signed arithmetic wraps in C
and C++ - it would be worse if they see it documented for some standards
and not others.

If there were enough benefit from the additional behaviour, that would
be fair enough. But there isn't any benefit of significance - correct
code remains correct after this change, and broken code remains broken.

Öö Tiib

unread,
Aug 5, 2019, 5:56:37 AM8/5/19
to
On Friday, 2 August 2019 21:46:30 UTC+3, David Brown wrote:
> On 02/08/2019 04:53, Öö Tiib wrote:
> > On Friday, 2 August 2019 00:26:16 UTC+3, David Brown wrote:
> >>
> >> Such optimisations won't be tricky - they will be completely
> >> impossible.
> >
> > Why? Making compiler to warn that "(x + z) > (y + z)" is maybe
> > sub-optimal code and to have some attribute or pragma for to
> > suppress it when wrap *was* reason to write it like that is
> > not impossible just tricky.
>
> It is impossible, because the code would no longer be correct (unless
> the compiler has other information about the ranges). If one of (x + z)
> or (y + z) wraps and the other does not, optimising to "x > y" would not
> be valid.
>
> Try this with gcc:
>
> bool foo(int x, int y, int z) {
> return (x + z) > (y + z);
> }
>
> #pragma GCC optimize "-fwrapv"
> bool foo2(int x, int y, int z) {
> return (x + z) > (y + z);
> }

The proposal basically is that "-fwrapv" is default. The
std::numeric_limits<int>::is_modulo must be true by default.
So one who wants the optimization need to use some kind of:

#pragma GCC optimize "-fno-wrapv"

The proposal does leave those optimizations beyond scope to keep
itself simple.

> Turning broken code with arbitrary bad behaviour into
> broken code with predictable bad behaviour is not particularly useful.

That is where I have different experience. At least 60% of effort put
into development seems to be about fixing defects and part of that cost
is caused by unreliability or instability of misbehavior that makes it
harder to figure what actually is wrong. Lot of debugging tools are
basically turning bad behavior that does who knows what into reliable
bad behavior that raises signals, throws exceptions, breaks or
terminates.

> So you think this feature - wrapping overflows - is so useful and
> important that it should be added to the language and forced upon all
> compilers, and yet it also is so unlikely to be correct code that
> compilers should warn about it whenever possible and require specific
> settings to disable the warning? Isn't that a little inconsistent?

Yes, wrapping feature makes logical defects to behave more predictably
and Yes, I consider it good. Yes, wrapping feature is sometimes useful
also on its own. Yes, there are compiler intrinsic functions so I can
live without the feature. Yes, I would still like warnings. Yes, I can
live without warnings. Yes, way to disable warnings can be good. Yes,
way to enable non-wrapping optimizations can be good. Yes, I can live
without non-wrapping optimizations in 95% of code and do those manually
in rest. I am not sure how it all is inconsistent. There just are
priorities what I favor more and these priorities are likely bit
different for all people.

> I'd be much happier to see some standardised pragmas like:
>
> #pragma STDC_OVERFLOW_WRAP
> #pragma STDC_OVERFLOW_TRAP
> #pragma STDC_OVERFLOW_UNDEFINED
>
> (or whatever variant is preferred)

That would be even better indeed, but what the proposal suggested
was simpler to implement and to add to standard leaving that possible
but beyond scope of it.

>
> Again, what do you think does not work with -fwrapv?

I have used it rarely and experimentally. It did sometimes optimize
int i loops when it should not. Yes, loop optimization might give up
to 10% performance difference on extreme case but that is again about
requiring some "-fno-wrapv" to allow compiler to do that optimization
not other way around. When currently "-fwrapv" is defective and
std::numeric_limits<int>::is_modulo is false then it is valid
to weasel out of each such case by saying that it is not a bug.

> > The well-defined behavior can not make wrong code to give
> > correct answers. The wrong answers will just come more consistently
> > and portably with wrap and so I can trust unit tests for my
> > math of embedded system ran on PC bit more.
> >
>
> If your unit tests rely on wrapping for overflow, then those unit tests
> are broken. "More consistent wrong answers" is /not/ a phrase you want
> to hear regarding test code! You want your embedded code to run quickly
> and efficiently - but there is no reason not to have
> -fsanitize=signed-integer-overflow for your PC-based unit tests and
> simulations.

I did not say that tests rely on undefined behavior or code relies on
undefined behavior. I mean that most actual code (including unit tests)
written by humans (and gods we can't hire) contains defects.
It reduces effort of finding and fixing when these defects behave more
uniformly. Usage of various debugging tools is good idea that helps
to reduce that effort too but is orthogonal to it and not in conflict.
Thanks, you have point there. If people will start to use that wrapping
behavior a lot for to achieve various effects then diagnosing it will
become more and more of false positive for those people.

I suspect that people will use it only on limited but important cases
(like for self-diagnosing or for cryptography). Other possible option
would to standardize compiler intrinsic functions for those cases.
That means the (sometimes surprising) optimizations will stay valid
by default. I haven't seen people objecting much when they then need
to mark or to rewrite questionable places to suppress false positive
diagnostics about well-defined code. I likely miss some depth of it
or am too naive about something else and it is hard to predict future.



David Brown

unread,
Aug 6, 2019, 8:32:59 AM8/6/19
to
There is a /huge/ difference between a flag that /adds/ semantics to the
C++ standard language, and one that /removes/ semantics.

Currently, gcc with an appropriate -std flag is quite close to standards
compliant for a variety of C++ (and C) standards. It is not perfect,
but AFAIK its as close as any other mainstream compiler. This is the
case regardless of -fwrapv or -fno-wrapv (the default). A user can
choose to add extra semantics to the language with -fwrapv if they find
it useful and are willing to pay the price in lost optimisations and
poorer safety and bug-catching tools.

But if C++20 /requires/ wrapping semantics, then there is no longer a
choice. If the compiler allows "-fno-wrapv", then (in that mode) it
will no longer be a compliant C++20 compiler. Code written correctly to
C++20 could fail to work correctly - and it could easily be silent
failures. (If you are using special modes, like "-no-exceptions", code
that relies on exceptions will have loud compile-time failures.) Such
silent non-compliance would not be acceptable.


>
>> Turning broken code with arbitrary bad behaviour into
>> broken code with predictable bad behaviour is not particularly useful.
>
> That is where I have different experience. At least 60% of effort put
> into development seems to be about fixing defects and part of that cost
> is caused by unreliability or instability of misbehavior that makes it
> harder to figure what actually is wrong. Lot of debugging tools are
> basically turning bad behavior that does who knows what into reliable
> bad behavior that raises signals, throws exceptions, breaks or
> terminates.

You misunderstand me. For debugging purposes, consistent bad behaviour
is useful. If the program crashes or does something weird in the same
place each test run, it is a lot easier to find and fix the problem.

But when you are testing your 16-bit MS-DOS apple cart program, and you
add your 32,768th apple, it doesn't matter if the program prints out the
balance as -32,768 apples or if it fails to print out anything - you can
find the problem.

With undefined behaviour on overflow, rather than wrapping behaviour,
there is a solid chance you will get consistent effects from any
particular compilation - but the details may change with other changes
to the code or other compilation options. What you don't get is
/predictable/ effects - but that doesn't matter for debugging. If you
had been predicting effects properly, you wouldn't have the bug in the
first place!

And with overflow being undefined behaviour, and therefore always an
error, you can use tools like gcc's sanitizer to give you a clear,
consistent and helpful debug message when the problem occurs.


Any tool or feature that makes it easier to find mistakes as early as
possible, is a good tool in my book. Having integer overflow as
undefined behaviour makes this easier - that is the most important
reason I have for wanting it. Wrapping overflow makes bugs /harder/ to
find. It increases the chance that the errors will go unnoticed, by
quietly continuing with invalid results.

>
>> So you think this feature - wrapping overflows - is so useful and
>> important that it should be added to the language and forced upon all
>> compilers, and yet it also is so unlikely to be correct code that
>> compilers should warn about it whenever possible and require specific
>> settings to disable the warning? Isn't that a little inconsistent?
>
> Yes, wrapping feature makes logical defects to behave more predictably
> and Yes, I consider it good.

That makes no sense. You prefer the behaviour of your defects to be
predictable? To be useful, that would mean you would have to know your
code has a defect - and in that case, surely you would fix the code
rather than wanting to run it with predictable errors?

The nearest use would be for debugging, where you want to work backwards
from the bad effect you get to figure out what caused it.
Predictability is sometimes useful then, but two's complement wrapping
is not nearly as helpful as trap on overflow behaviour - which would be
impossible when you have wrapping as part of the language definition.

> Yes, wrapping feature is sometimes useful
> also on its own.

Occasionally, yes. It happens often enough that it is important the
language has this capability. It happens rarely enough that it is not a
problem to have somewhat ugly code to do it. For all platforms where
you have two's complement representation of signed integers, you can
handle this with conversions to and from unsigned types. It's not hard
to wrap it all in a simple class if you want neater code.

> Yes, there are compiler intrinsic functions so I can
> live without the feature.

Unsigned arithmetic is not a compiler intrinsic. You don't need to
detect overflow to get wrapping behaviour.

(Having said that, I would like to see some standard library classes
that cover the features of many common intrinsics, such as gcc and
clang's overflow builtins.)

> Yes, I would still like warnings. Yes, I can
> live without warnings. Yes, way to disable warnings can be good. Yes,
> way to enable non-wrapping optimizations can be good.

If wrapping behaviour is required for the language standard, you won't
get these.

> Yes, I can live
> without non-wrapping optimizations in 95% of code and do those manually
> in rest. I am not sure how it all is inconsistent. There just are
> priorities what I favor more and these priorities are likely bit
> different for all people.
>

What is inconsistent is to want a feature that is almost always an
indication of incorrect code.

>> I'd be much happier to see some standardised pragmas like:
>>
>> #pragma STDC_OVERFLOW_WRAP
>> #pragma STDC_OVERFLOW_TRAP
>> #pragma STDC_OVERFLOW_UNDEFINED
>>
>> (or whatever variant is preferred)
>
> That would be even better indeed, but what the proposal suggested
> was simpler to implement and to add to standard leaving that possible
> but beyond scope of it.
>

The proposal suggests something that I am convinced is a huge step
backwards (making wrapping behaviour required), while failing to provide
a simple, standardised way to let programmers make choices.

(I note the proposal suggests, as you do, that compilers could still
have flags like "-fno-wrapv". To me, this shows that the proposal
authors are making the same mistake you are about the consequences of
changing the semantics of the language.)

>>
>> Again, what do you think does not work with -fwrapv?
>
> I have used it rarely and experimentally. It did sometimes optimize
> int i loops when it should not.

That tells me nothing, I am afraid.

> Yes, loop optimization might give up
> to 10% performance difference on extreme case but that is again about
> requiring some "-fno-wrapv" to allow compiler to do that optimization
> not other way around. When currently "-fwrapv" is defective and

Without further references than a vague memory of something that wasn't
quite what you expected, I will continue to assume that "-fwrapv" is
/not/ defective and works exactly as it says it will. Show me examples,
bug reports, or something more definite and I will change that
assumption. As I have said before, no one claims gcc is bug-free.

> std::numeric_limits<int>::is_modulo is false then it is valid
> to weasel out of each such case by saying that it is not a bug.

In gcc, is_modulo is false because ints are not guaranteed to be
wrapping. They /might/ have wrapping behaviour, depending on the flags
and the code in question, but won't necessarily have it.

Should the value of is_modulo be dependent on whether -fwrapv is in
force or not? I don't think so - I think making these features
dependent on compiler settings would open a large can of worms.
Remember, you are free to change the -fwrapv setting in the middle of a
file using pragmas, or with function attributes - I would not like to
see is_modulo changing to fit. It is better to keep it at the
pessimistic "false" setting. (That is, at least, my opinion on this
particular matter - but I can see why some people could think differently.)

>
>>> The well-defined behavior can not make wrong code to give
>>> correct answers. The wrong answers will just come more consistently
>>> and portably with wrap and so I can trust unit tests for my
>>> math of embedded system ran on PC bit more.
>>>
>>
>> If your unit tests rely on wrapping for overflow, then those unit tests
>> are broken. "More consistent wrong answers" is /not/ a phrase you want
>> to hear regarding test code! You want your embedded code to run quickly
>> and efficiently - but there is no reason not to have
>> -fsanitize=signed-integer-overflow for your PC-based unit tests and
>> simulations.
>
> I did not say that tests rely on undefined behavior or code relies on
> undefined behavior. I mean that most actual code (including unit tests)
> written by humans (and gods we can't hire) contains defects.
> It reduces effort of finding and fixing when these defects behave more
> uniformly. Usage of various debugging tools is good idea that helps
> to reduce that effort too but is orthogonal to it and not in conflict.

I accept what you are saying, but I think you are wrong. I disagree
that wrapping overflow is useful for debugging, as I explained earlier.

However, compilers like gcc give you the choice. Add "-fwrapv" when you
find it helps with debugging - just as you might add sanitizer options
and warning options. It does not have to be part of the language, which
would reduce choice and options and limit your debugging tools.
Indeed.

A key problem with relying on wrapping behaviour is that it is very
subtle - it is typically invisible in the code, without studying it in
depth.

> I suspect that people will use it only on limited but important cases
> (like for self-diagnosing or for cryptography).

Cryptography would typically use unsigned types.

Having library features for detecting overflow would be clearer, safer,
and more portable than relying on wrapping behaviour for those that want
to check for problems after they have done their arithmetic that might
overflow. I really dislike the concept of running into the traffic and
then checking if you have been run over, instead of looking first and
crossing when it is safe. It would be better to have types similar to
std::optional which track the validity of operations - letting you
happily perform arithmetic and at the end check for any failures.

A big issue I have with the whole concept is that currently we have
/types/ for which overflow is defined (unsigned types) and types for
which it is not defined. But overflow behaviour should be part of the
operations, not the types.


> Other possible option
> would to standardize compiler intrinsic functions for those cases.

I would say making a standard library section that has the required
behaviour - compilers can implement this using intrinsics or builtins if
they like.

> That means the (sometimes surprising) optimizations will stay valid
> by default. I haven't seen people objecting much when they then need
> to mark or to rewrite questionable places to suppress false positive
> diagnostics about well-defined code. I likely miss some depth of it
> or am too naive about something else and it is hard to predict future.
>

Predictions are hard, especially about the future!


David Brown

unread,
Aug 6, 2019, 8:54:48 AM8/6/19
to
On 31/07/2019 10:14, Bonita Montero wrote:
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r0.html
>

I've done a little digging, and there are several revisions to that
paper. The latest (AFAICS) is:

<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r4.html>


One change in later revisions that is relevant to some parts of this
thread is:

"""
*Status-quo* If a signed operation would naturally produce a value that
is not within the range of the result type, the behavior is undefined.
The author had hoped to make this well-defined as wrapping (the
operations produce the same value bits as for the corresponding unsigned
type), but WG21 had strong resistance against this.
"""

It seems I am not the only one who thinks this way.

I think this revision will be a lot less controversial - it mostly
documents the way current compilers work on current hardware, and
simplifies the wording in the standard to match.



I found another related paper:

<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1236r1.html>

I think the two papers cover the same ideas, but with different wording.
I don't know what will actually end up in C++20.

Manfred

unread,
Aug 7, 2019, 10:05:56 AM8/7/19
to
Premise: I agree with you on most of your arguments about overflow.

I also note that the authors do explicitly define wrapping for signed
integers (section 2, [basic.fundamental]), but also explicitly leave out
of scope (section 3) /compiler/ behaviour with wrapping on signed
integers, which makes the proposal inherently flawed, I believe.
It sounds like they know there is a problem with the matter, yet they
want signed integer wrapping as defined behaviour, but do not want to
address the consequences of this decision.


On 8/6/2019 2:32 PM, David Brown wrote:
> A big issue I have with the whole concept is that currently we have
> /types/ for which overflow is defined (unsigned types) and types for
> which it is not defined. But overflow behaviour should be part of the
> operations, not the types.

Here I tend to disagree, or at least I am fine with the behaviour being
attached to the types.
You are probably recalling that in ASM overflow is part of the
instruction, but I think this is because the ASM type system is
obviously much more rudimentary than in higher level languages.
Programming languages are meant to translate human logic into machine
instructions, and we are used to operations that behave differently
depending on the type they are performed on - see e.g. addition on real
and complex numbers.
From this perspective, in binary arithmetic it does make sense that
addition behaves differently for signed and unsigned integers.
On the other hand, having some sort of "+" and "ǂ" would complicate the
syntax, and be overly redundant too: you would have to specify the
behavior of non-wrapping addition on unsigned integers, and wrapping
addition on signed integers as well - this would bring more confusion
than help, IMHO.

David Brown

unread,
Aug 7, 2019, 11:01:14 AM8/7/19
to
On 07/08/2019 16:05, Manfred wrote:
> Premise: I agree with you on most of your arguments about overflow.
>
> I also note that the authors do explicitly define wrapping for signed
> integers (section 2, [basic.fundamental]), but also explicitly leave out
> of scope (section 3) /compiler/ behaviour with wrapping on signed
> integers, which makes the proposal inherently flawed, I believe.
> It sounds like they know there is a problem with the matter, yet they
> want signed integer wrapping as defined behaviour, but do not want to
> address the consequences of this decision.
>

Yes. But the link was to the first draft of the proposal - I made
another post with a link to a later version, where the idea of defining
signed overflow is dropped.

>
> On 8/6/2019 2:32 PM, David Brown wrote:
>> A big issue I have with the whole concept is that currently we have
>> /types/  for which overflow is defined (unsigned types) and types for
>> which it is not defined.  But overflow behaviour should be part of the
>> operations, not the types.
>
> Here I tend to disagree, or at least I am fine with the behaviour being
> attached to the types.

I think it would be unpractical, in general, to have overflow behaviour
attached to operations rather than types - but it is the operations that
have the behaviour.

It would be entirely possible to put together classes and some operator
overloads that would let you write things like :

int a, b, c;

c = a +wrapping+ b;
c = a -saturating- b;

and so on.

But I suspect people would find that too verbose for most uses. Hence
we have the current solution.


> You are probably recalling that in ASM overflow is part of the
> instruction, but I think this is because the ASM type system is
> obviously much more rudimentary than in higher level languages.

It was not what I was thinking of, no. (There are several reasons for
assembly arithmetic operations working the way they do and having the
flags they do, at least on some cpus.)

> Programming languages are meant to translate human logic into machine
> instructions, and we are used to operations that behave differently
> depending on the type they are performed on - see e.g. addition on real
> and complex numbers.
> From this perspective, in binary arithmetic it does make sense that
> addition behaves differently for signed and unsigned integers.

Certainly some aspects of behaviour have to depend on the operand types
and the result types. But the behaviour is not fully defined by them.
In mathematics, when you divide two integers you can decide if you want
the result rounded/truncated to an integer, or expressed as a rational,
or perhaps as a real number. It is the operation that determines this,
not the operand types. When you have two unsigned integers and subtract
them, you could decide the result should be a signed integer rather than
an unsigned integer - it is the operation that determines it. Your
choice of wrapping, saturating, trapping, ignoring overflow, etc., is a
matter of the operation, independent of the types.

For practical reasons (which I am mostly happy with), C says that when
the operands are unsigned types (after integer promotion) the operation
is carried out as a wrapping operation, while for signed types (after
promotion), overflow is UB.

> On the other hand, having some sort of "+" and "ǂ" would complicate the
> syntax, and be overly redundant too: you would have to specify the
> behavior of non-wrapping addition on unsigned integers, and wrapping
> addition on signed integers as well - this would bring more confusion
> than help, IMHO.

I agree that practicality forces the language to use types to determine
the operations you get from +, -, etc. But the overflow behaviour is
part of the operation, not the type.

Alf P. Steinbach

unread,
Aug 7, 2019, 12:39:18 PM8/7/19
to
On 07.08.2019 17:00, David Brown wrote:
> [snip]
> I think it would be unpractical, in general, to have overflow behaviour
> attached to operations rather than types - but it is the operations that
> have the behaviour.

Consider in Python,

x = a/b

versus

x = a//b

... where the former is always, reliably, floating point division, and
the latter is always, reliably, integer division. IMO that's nice. The
C++ way with operator behavior influenced by types just trips up people.


> It would be entirely possible to put together classes and some operator
> overloads that would let you write things like :
>
> int a, b, c;
>
> c = a +wrapping+ b;
> c = a -saturating- b;
>
> and so on.

For this I would consider the C# `checked` keyword.

<url:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/checked>

There is AFAIK nothing corresponding to saturation, but by default
integer arithmetic is modulo. Using the `checked` keyword like

checked { c = a + b; }

... one specifies overflow checking, with a well defined exception,
namely `System.OverflowException`, in case of overflow.

The default in C++ is instead UB for overflow. So in C++ one could also
have keyword `wrapping`. I.e, keywords/contexts `wrapping` and
`checked`, plus maybe `unchecked` for the case where someone uses a
compiler option to change default to `wrapping` or `checked`, but one
really wants the possible optimization and efficiency of guaranteed UB.


> But I suspect people would find that too verbose for most uses.

Yeah, but it's like the old proof that doors are practically impossible,
by envisioning one particular door that's obviously very impractical.

For doors there is existence proof that they're not practically impossible.

And ditto for type-independent reliable operator behavior, in particular
the C# approach.


> Hence we have the current solution.

No, for sure. But more my opinion: it's more historical, that some
decades ago the optimization possibilities one could give the compiler
for stuff like this, did matter. Today there is existence proof, in
particular of Java outperforming C++ in certain cases, that it not only
does not matter but can be a directly counter-productive approach.

[snip more]


Cheers!,

- Alf

David Brown

unread,
Aug 7, 2019, 3:15:46 PM8/7/19
to
On 07/08/2019 18:39, Alf P. Steinbach wrote:
> On 07.08.2019 17:00, David Brown wrote:
>> [snip] I think it would be unpractical, in general, to have overflow
>> behaviour
>> attached to operations rather than types - but it is the operations that
>> have the behaviour.
>
> Consider in Python,
>
>     x = a/b
>
> versus
>
>     x = a//b
>
> ... where the former is always, reliably, floating point division, and
> the latter is always, reliably, integer division. IMO that's nice. The
> C++ way with operator behavior influenced by types just trips up people.

I don't think it does trip up people - but the Python method certainly
causes confusion. The Python method is okay when you get used to it,
but it does not scale well - you cannot sensibly have operators for
every variation of operation people might want.

Where people sometimes get mixed up in C and C++, I believe, is when
there is a mix between signed and unsigned types, or for unsigned types
that are smaller than int.

>
>
>> It would be entirely possible to put together classes and some operator
>> overloads that would let you write things like :
>>
>>     int a, b, c;
>>
>>     c = a +wrapping+ b;
>>     c = a -saturating- b;
>>
>> and so on.
>
> For this I would consider the C# `checked` keyword.
>
> <url:
> https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/checked>
>

Eek! Trust Microsoft to take an interesting idea and turn it into an
inconsistent jumble, with the worst choice of default.

>
> There is AFAIK nothing corresponding to saturation, but by default
> integer arithmetic is modulo. Using the `checked` keyword like
>
>     checked { c = a + b; }
>
> ... one specifies overflow checking, with a well defined exception,
> namely `System.OverflowException`, in case of overflow.
>
> The default in C++ is instead UB for overflow. So in C++ one could also
> have keyword `wrapping`. I.e, keywords/contexts `wrapping` and
> `checked`, plus maybe `unchecked` for the case where someone uses a
> compiler option to change default to `wrapping` or `checked`, but one
> really wants the possible optimization and efficiency of guaranteed UB.
>

There is an inkling of an idea in there, but it needs a fair amount of
thought before you can get something that is going to be clear and
scalable. Keywords won't be sufficient - there are going to be too many
options, and you want something that can be defined as part of a library
rather than built into the language. The idea of being able to say
"apply these options to this block of code" has too many potential uses
to waste on a few fixed keywords. The same syntax could be used for
controlling optimisation, for giving all accesses an atomic memory
order, or many other things.

>
>> But I suspect people would find that too verbose for most uses.
>
> Yeah, but it's like the old proof that doors are practically impossible,
> by envisioning one particular door that's obviously very impractical.
>

That analogy is lost on me, I'm afraid.

> For doors there is existence proof that they're not practically impossible.
>
> And ditto for type-independent reliable operator behavior, in particular
> the C# approach.
>
>
>>  Hence we have the current solution.
>
> No, for sure. But more my opinion: it's more historical, that some
> decades ago the optimization possibilities one could give the compiler
> for stuff like this, did matter. Today there is existence proof, in
> particular of Java outperforming C++ in certain cases, that it not only
> does not matter but can be a directly counter-productive approach.
>

Sorry, I failed to follow this too. You seem, I think, to be saying
that because some languages are slower than others then it is okay for
C++ to become slower. I don't agree at all, but if you think overflow
behaviours is all about efficiency then you have missed several
important points.

David Brown

unread,
Aug 7, 2019, 3:20:47 PM8/7/19
to
On 07/08/2019 18:52, Stefan Ram wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
>> Consider in Python,
>> x = a/b
>> versus
>> x = a//b
>
> In VBA it's
>
> x = a/b
>
> vs
>
> x = a\b
>
> which has the elegance of using the same symbols as used in
> mathematics. (A backslash-like symbol, in mathematics, is
> used for set difference, quotient group, and integral division.)
>

Backslash is used for set difference and a few other things in
mathematics. Quotient groups use forward slash in my experience as does
integer division. So I don't see any logic here at all. However,
sometimes mathematical symbol conventions vary by language - I have no
idea about conventions in German.

Other division symbols that I do know are used "a : b" and "a ÷ b".

Ralf Goertz

unread,
Aug 8, 2019, 6:14:53 AM8/8/19
to
Am Wed, 7 Aug 2019 21:20:36 +0200
schrieb David Brown <david...@hesbynett.no>:

> Backslash is used for set difference and a few other things in
> mathematics. Quotient groups use forward slash in my experience as
> does integer division. So I don't see any logic here at all.

I have the same experience. Maybe apart from integer division. I haven't
seen the forward slash as indicator of *integer* division but then I am
usually more interested with the remainder than the quotient. :-)

> However, sometimes mathematical symbol conventions vary by language -
> I have no idea about conventions in German.

The conventions in German don't differ from those in English in this
respect AFAIK.

Ralf Goertz

unread,
Aug 8, 2019, 6:21:26 AM8/8/19
to
Am Wed, 7 Aug 2019 21:20:36 +0200
schrieb David Brown <david...@hesbynett.no>:

> Backslash is used for set difference and a few other things in
> mathematics. Quotient groups use forward slash in my experience as
> does integer division. So I don't see any logic here at all.

I have the same experience. Maybe apart from integer division. I haven't
seen the forward slash as indicator of *integer* division but then I am
usually more interested in the remainder than the quotient. :-)

> However, sometimes mathematical symbol conventions vary by language -
> I have no idea about conventions in German.

0 new messages