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

Integer arithmetic when overflow exists

284 views
Skip to first unread message

junyangzou

unread,
Oct 7, 2013, 11:39:59 PM10/7/13
to
Tow 32 bit integer values A and B, are processed to give the 32 bit integers C and D as per the following rules. Which of the rule(s) is(are) reversible? i.e. is it possible to obtain A and B given c and D in all condition?

A. C = (int32)(A+B), D = (int32)(A-B)

B. C = (int32)(A+B), D= (int32)((A-B)>>1)

C. C = (int32)(A+B), D = B

D. C = (int32)(A+B), D = (int32)(A+2*B)

E. C = (int32)(A*B), D = (int32)(A/B)

A few questions about the integer arithmetic. Modular addition forms amathematical structure known as an abelian group. How about signed addition? It's also commutative (that’s where the “abelian” part comes in) and associative, is this forms a n an abelian group?

Given that integer addition is commutative and associative, C is apparently true, because we can retrieve A by (A+(B-B)). What about D? Can we assume that 2 * B = B + B st. B = A+B+B-(A+B)?

And multiplication is more complicated, but I know that it can not be retrieve A if there is an overflow.

alf.p.s...@gmail.com

unread,
Oct 8, 2013, 12:31:07 AM10/8/13
to
On Tuesday, October 8, 2013 5:39:59 AM UTC+2, junyangzou wrote:
> Tow 32 bit integer values A and B, are processed to give the 32 bit integers C and D as per the following rules. Which of the rule(s) is(are) reversible? i.e. is it possible to obtain A and B given c and D in all condition?
>
> A. C = (int32)(A+B), D = (int32)(A-B)
> B. C = (int32)(A+B), D= (int32)((A-B)>>1)
> C. C = (int32)(A+B), D = B
> D. C = (int32)(A+B), D = (int32)(A+2*B)
> E. C = (int32)(A*B), D = (int32)(A/B)

This sounds very much like homework.


> A few questions about the integer arithmetic. Modular addition forms amathematical structure known as an abelian group. How about signed addition?

Whether C++ integer addition is modular depends FORMALLY on the implementation, and can be checked via std::numeric_limits.

In practice it's modular on all modern systems, but at least one compiler (namely g++) uses the formal UB for overflow, supporting archaic systems, as an excuse to do rather inconvenient things, like "optimizations". You will most proabbly stop g++ from doing that by using the option "-fwrapv". This informs that compiler that you do want the practical and efficient machine code level modular arithmetic behavior, two's complement form.

> It's also commutative (that’s where the “abelian” part comes in) and associative, is this forms a n an abelian group?

If you don't know what an Abelian group is, how will it help you to get answer about whether arithmetic forms such group?


> Given that integer addition is commutative and associative, C is apparently true, because we can retrieve A by (A+(B-B)). What about D? Can we assume that 2 * B = B + B st. B = A+B+B-(A+B)?

Only for an implementation where signed arithmetic is modular.


> And multiplication is more complicated, but I know that it can not be retrieve A if there is an overflow.

That sounds a bit too pessimistic. :-)


Cheers & hth.,

- Alf

junyangzou

unread,
Oct 8, 2013, 2:16:29 AM10/8/13
to
> This sounds very much like homework.

Hah, actually it is a test question from Microsoft intern hiring written test.

> If you don't know what an Abelian group is, how will it help you to get answer about whether arithmetic forms such group?

AFAIK, commutative and associative is a sufficient for an operation to form a Abelian group.


And can I understand that under two's complement, C and D is the right answer?

David Brown

unread,
Oct 8, 2013, 5:28:45 AM10/8/13
to
On 08/10/13 06:31, alf.p.s...@gmail.com wrote:
> On Tuesday, October 8, 2013 5:39:59 AM UTC+2, junyangzou wrote:
>
>> A few questions about the integer arithmetic. Modular addition
>> forms amathematical structure known as an abelian group. How about
>> signed addition?
>
> Whether C++ integer addition is modular depends FORMALLY on the
> implementation, and can be checked via std::numeric_limits.
>
> In practice it's modular on all modern systems, but at least one
> compiler (namely g++) uses the formal UB for overflow, supporting
> archaic systems, as an excuse to do rather inconvenient things, like
> "optimizations". You will most proabbly stop g++ from doing that by
> using the option "-fwrapv". This informs that compiler that you do
> want the practical and efficient machine code level modular
> arithmetic behavior, two's complement form.

gcc will also use "practical and efficient machine code" when -fwrapv is
not in effect - noting that signed overflow is undefined behaviour lets
the compiler write more efficient code in some cases, but it never means
writing /worse/ code.

There are some occasions when it is useful to rely on modular behaviour
with signed integers - in which case "-fwrapv" is useful with gcc. Of
course, this is undefined behaviour in the C standards - you have to be
sure that the compiler you are using does give defined results. Most
compilers - including gcc with "-fwrapv" turned off - will give you
modular behaviour because that's the smallest and fastest code, but not
because they guarantee it. They will happily optimise code assuming
integer overflow is UB.

For example, if you write this code:

int a = 0x7fffffff; // Assuming 32-bit ints for simplicity
int b;
int foo(void) {
if (a >= 0) {
b = a + 1;
} else {
b = -a + 1;
}
if (b > 0) {
return 1;
} else {
return 0;
}
}

On any modern system, b will get the value 0x80000000, or -1. But if
(good enough) optimisation is enabled, a good compiler will skip the
comparison and return 1. This is not an "inconvenient optimisation", as
though such things existed, but a correct and standards-compliant
implementation.


alf.p.s...@gmail.com

unread,
Oct 8, 2013, 9:22:46 AM10/8/13
to
On Tuesday, October 8, 2013 11:28:45 AM UTC+2, David Brown wrote:
> On 08/10/13 06:31, alf.p.s...@gmail.com wrote:
> > On Tuesday, October 8, 2013 5:39:59 AM UTC+2, junyangzou wrote:
>
> >> A few questions about the integer arithmetic. Modular addition
> >> forms amathematical structure known as an abelian group. How about
> >> signed addition?
> >
>
> > Whether C++ integer addition is modular depends FORMALLY on the
> > implementation, and can be checked via std::numeric_limits.

Oops, I didn't mean that.

s/integer/signed integer/

Unsigned integer arithmetic is always modular in C and C++.


> > In practice it's modular on all modern systems, but at least one
> > compiler (namely g++) uses the formal UB for overflow, supporting
> > archaic systems, as an excuse to do rather inconvenient things, like
> > "optimizations". You will most proabbly stop g++ from doing that by
> > using the option "-fwrapv". This informs that compiler that you do
> > want the practical and efficient machine code level modular
> > arithmetic behavior, two's complement form.
>
> gcc will also use "practical and efficient machine code" when -fwrapv is
> not in effect

Oh yes, it has no choice there. That's all there is, now that the Univac series of computers is no longer extant. As you rightly point out, the difference is merely whether gcc is allowed to ASSUME that it could be using something else than it's actually using, or that that would fine by the programmer.


> - noting that signed overflow is undefined behaviour lets
> the compiler write more efficient code in some cases, but it never means
> writing /worse/ code.

Needless to say, the assumption that's contrary to reality, if one lets the compiler make it (which it's more than eager to do), leads to sometimes strange & baffling, and altogether impractical and time-wasting, results.

Personally I think of these results as incorrect machine code, even if formally permitted, and incorrect is IMO very much "worse" than not optimally efficient. Yes, I do understand that you meant "worse" in the context of what I wrote, i.e. pertaining to efficiency, and that's true, the code isn't worse in that respect, and I should not have given that misleading impression, sorry. Still, correctness, not just wrt. formal rules, is more important.

g++ is (rightly, IMHO) by some / many regarded as a "perverse" compiler, generally exploiting every little formal loop-hole to do the unexpected and impractical. I.e., doing small and insignificant little micro-optimizations at huge and overwhelming global cost. Balancing that -- and "better the Devil you know" etc. -- it's generally far more up-to-date and correct wrt. the core language parts of the standard than e.g. Visual C++.


Cheers,

- Alf (noting that the holiness of g++ is a religious issue, heh :-) )

David Brown

unread,
Oct 8, 2013, 11:15:50 AM10/8/13
to
On 08/10/13 15:22, alf.p.s...@gmail.com wrote:
> On Tuesday, October 8, 2013 11:28:45 AM UTC+2, David Brown wrote:
>> On 08/10/13 06:31, alf.p.s...@gmail.com wrote:
>>> On Tuesday, October 8, 2013 5:39:59 AM UTC+2, junyangzou wrote:
>>
>>>> A few questions about the integer arithmetic. Modular addition
>>>> forms amathematical structure known as an abelian group. How
>>>> about signed addition?
>>>
>>
>>> Whether C++ integer addition is modular depends FORMALLY on the
>>> implementation, and can be checked via std::numeric_limits.
>
> Oops, I didn't mean that.
>
> s/integer/signed integer/
>
> Unsigned integer arithmetic is always modular in C and C++.

I assumed that's what you meant.

>
>>> In practice it's modular on all modern systems, but at least one
>>> compiler (namely g++) uses the formal UB for overflow,
>>> supporting archaic systems, as an excuse to do rather
>>> inconvenient things, like "optimizations". You will most proabbly
>>> stop g++ from doing that by using the option "-fwrapv". This
>>> informs that compiler that you do want the practical and
>>> efficient machine code level modular arithmetic behavior, two's
>>> complement form.
>>
>> gcc will also use "practical and efficient machine code" when
>> -fwrapv is not in effect
>
> Oh yes, it has no choice there. That's all there is, now that the
> Univac series of computers is no longer extant. As you rightly point
> out, the difference is merely whether gcc is allowed to ASSUME that
> it could be using something else than it's actually using, or that
> that would fine by the programmer.
>
>
>> - noting that signed overflow is undefined behaviour lets the
>> compiler write more efficient code in some cases, but it never
>> means writing /worse/ code.
>
> Needless to say, the assumption that's contrary to reality, if one
> lets the compiler make it (which it's more than eager to do), leads
> to sometimes strange & baffling, and altogether impractical and
> time-wasting, results.

It's only strange and baffling if you have written strange and baffling
code. Most people assume "int" works like a mathematical integer - not
modular arithmetic. If you have an integer "x" which you know is
non-negative, then you will assume that adding 1 to it will give you a
positive integer. That's the way maths works. Why shouldn't the
compiler make the same assumption?

The only time the compiler will get it "wrong" (meaning "contrary to the
user's expectations" rather than "contrary to the standards") by
treating signed overflow as undefined behaviour is if you have code that
specifically relies on modular arithmetic of integers. I suspect (from
gut-feeling) that such code would often be better written using unsigned
integers rather than signed ones - that would make more sense to me, and
be correct according to the standards. (For the kind of code I write,
unsigned integers are more common than signed ones, so I could be biased.)


Can you give examples of code that relies directly on the modular nature
of two's compliment integers and which could not be done just as easily,
and safer, using unsigned integers? If not, then having signed overflow
as undefined behaviour just gives your compiler greater freedom to
generate smaller and faster code without compromising the expected
functionality of the code.

>
> Personally I think of these results as incorrect machine code, even
> if formally permitted, and incorrect is IMO very much "worse" than
> not optimally efficient. Yes, I do understand that you meant "worse"
> in the context of what I wrote, i.e. pertaining to efficiency, and
> that's true, the code isn't worse in that respect, and I should not
> have given that misleading impression, sorry. Still, correctness, not
> just wrt. formal rules, is more important.

I certainly agree that correctness trumps efficiency every time. And
/expected/ behaviour is important, not just standards dictated behaviour.

I just can't see where you would get a conflict with the expected
behaviour in real-world code here.

>
> g++ is (rightly, IMHO) by some / many regarded as a "perverse"
> compiler, generally exploiting every little formal loop-hole to do
> the unexpected and impractical. I.e., doing small and insignificant
> little micro-optimizations at huge and overwhelming global cost.

I haven't seen that as a particular problem with gcc. I have seen it as
a general issue as compilers have got better optimisers - programmers
have had to become more familiar with the details of how the language
actually works. For example, alias analysis has lead to faster code
from many toolchains, including gcc, at the cost of breaking code with
some kinds of unsafe typecasting practices. As an embedded developer, I
follow a number of mailing lists and groups for embedded compilers - it
is common to see posts saying "the compiler's optimiser is broken. My
code works fine with -O0, but fails at -O2". It is rare indeed that the
problem lies with the compiler - it is the user's code that is at fault.

What would you rather do? Disable optimisations that conflict with
/your/ particular idea of "expected behaviour" regardless of the
standards? Where do you draw the line? Should the compiler have a
"-fI-know-what-I-am-doing" flag?

The irony, of course, is that gcc /has/ such a flag in this case -
"-fstrict-overflow". But it is turned on by -O2 and above, and you
don't need to know what you are doing to enable -O2 optimisation!

More helpfully, I would like to see the warnings in gcc improved (they
are currently better than any other compiler I have used, but I always
want more!) and many more warnings enabled by default. This sort of
things is an example - gcc has a warning "-Wstrict-overflow", but it is
not enabled without -Wall. I think a great deal of C and C++ code would
be far higher quality, and far fewer bugs, if compilers defaulted to
"-Wall -Wextra -Werror" (or the equivalent for non-gcc compilers).

alf.p.s...@gmail.com

unread,
Oct 8, 2013, 12:22:05 PM10/8/13
to
On Tuesday, October 8, 2013 5:15:50 PM UTC+2, David Brown wrote:
> On 08/10/13 15:22, alf.p.s...@gmail.com wrote:
>
> > Needless to say, the assumption that's contrary to reality, if one
> > lets the compiler make it (which it's more than eager to do), leads
> > to sometimes strange & baffling, and altogether impractical and
> > time-wasting, results.
>
> It's only strange and baffling if you have written strange and baffling
> code.

Sorry, no, it's far worse. Like wholesale removal of if-blocks and loops.


> Most people assume "int" works like a mathematical integer - not
> modular arithmetic.

Hm, don't know about "most people".

A Python 3.x programmer might think so.

However, no competent C or C++ programmer assumes that.

Making that assumption might even be used as a defining criterion for incompetence and/or newbieness.

Along with writing "void main", which you will find in examples all over Microsoft's documentation.


> If you have an integer "x" which you know is
> non-negative, then you will assume that adding 1 to it will give you a
> positive integer.

No, all my reviews, when I worked (and now that I'm almost fully healed from surgeries I may just start working again, if employers are not scared away by long absence), said that I was extremely competent, not the opposite.

So no, I would absolutely not make an assumption flagrantly in contradiction with reality.


> That's the way maths works. Why shouldn't the
> compiler make the same assumption?

Because that's not the way that integer arithmetic works in C++, and because, far more importantly, because making and exploiting that assumption produces effectively incorrect machine code, even if formally allowed.


> The only time the compiler will get it "wrong" (meaning "contrary to the
> user's expectations" rather than "contrary to the standards") by
> treating signed overflow as undefined behaviour is if you have code that
> specifically relies on modular arithmetic of integers.

Possibly right, but no point in debating that I think.

Relying on modular arithmetic is pretty common.

Because all integer arithmetic in C++ is now in practice modular (no Univac any more), and because for efficiency reasons trapping on integer overflow is in practice not used.


> I suspect (from
> gut-feeling) that such code would often be better written using unsigned
> integers rather than signed ones - that would make more sense to me, and
> be correct according to the standards.

I agree.


> (For the kind of code I write,
> unsigned integers are more common than signed ones, so I could be biased.)
>
> Can you give examples of code that relies directly on the modular nature
> of two's compliment integers and which could not be done just as easily,
> and safer, using unsigned integers? If not, then having signed overflow
> as undefined behaviour just gives your compiler greater freedom to
> generate smaller and faster code without compromising the expected
> functionality of the code.

No, it gives the compiler freedom to waste the programmer's time figuring out why e.g. some loop's code is never executed: silent and time-wasting changes of the code's meaning.

It is completely unnecessary and completely fails to weight a local efficiency micro-advantage against far much important predictability and correctness.


[snip]
>
> I just can't see where you would get a conflict with the expected
> behaviour in real-world code here.

Right, it's necessarily at least as rare as the "optimization" opportunity itself.

And so to the degree that one can argue that the formal loophole exploitation will not produce a problem (oh it's so very rare!), one can equally argue that the "optimization" is worthless, wasted work, and that it's therefore necessarily at the cost of some real world improvement to the compiler.


> What would you rather do? Disable optimisations that conflict with
> /your/ particular idea of "expected behaviour" regardless of the
> standards?

There are two incorrect assumptions in that question.

The first incorrect assumption, that it's subjective to expect a now universal-for-C-and-C++ arithmetic behavior (not even gcc behaves differently unless one asks for trapping). It's not subjective. It's the reality.

The second incorrect assumption, that a compiler team is free to exploit formal loopholes that are in the standard in support of now archaic systems, regardless of practical consequences. The cost for something that occurs very seldom is low, but it is there. The waste of programmer's valuable time, and the mere EXISTENCE of such, which is noticed and remarked on, and even used to produce clever blog articles "guess what this code does" and the like, translates directly into a negative perception of the compiler.


> Where do you draw the line? Should the compiler have a
> "-fI-know-what-I-am-doing" flag?

It should merely be PREDICTABLE, doing by default the most practical thing, as opposed to the most impractical, time-wasting and unimaginable thing.

So it's an easy line to draw in most cases. ;-)


> The irony, of course, is that gcc /has/ such a flag in this case -
> "-fstrict-overflow". But it is turned on by -O2 and above, and you
> don't need to know what you are doing to enable -O2 optimisation!

It's no irony that gcc has a lot of flags that impact its behavior and renders it needlessly unpredictable: it is merely very sad. :(

By choice I am unfamiliar with most such flags.

I look them up when necessary, but dang if I should let the needless compiler complexity use up my time.


[snip]

Cheers,

- Alf

Paavo Helde

unread,
Oct 8, 2013, 12:55:04 PM10/8/13
to
alf.p.s...@gmail.com wrote in
news:881daf99-67bf-4256...@googlegroups.com:

> On Tuesday, October 8, 2013 5:15:50 PM UTC+2, David Brown wrote:
>> On 08/10/13 15:22, alf.p.s...@gmail.com wrote:

>> Most people assume "int" works like a mathematical integer - not
>> modular arithmetic.
>
> Hm, don't know about "most people".
>
> A Python 3.x programmer might think so.
>
> However, no competent C or C++ programmer assumes that.

Right, competent programmers know that overflow in signed arithmetics is UB
and do not rely on it in portable code. Even if it appears to work in some
particular way now and is not trapped, nobody guarantees the same in the
next version of gcc.

If they want modular arithmetics, they use unsigned integers, what could be
simpler?

Cheers
Paavo

alf.p.s...@gmail.com

unread,
Oct 8, 2013, 2:50:46 PM10/8/13
to
On Tuesday, October 8, 2013 6:55:04 PM UTC+2, Paavo Helde wrote:
> alf.p.s...@gmail.com wrote in
> news:881daf99-67bf-4256...@googlegroups.com:
> > On Tuesday, October 8, 2013 5:15:50 PM UTC+2, David Brown wrote:
> >> On 08/10/13 15:22, alf.p.s...@gmail.com wrote:
>
> >> Most people assume "int" works like a mathematical integer - not
> >> modular arithmetic.
>
> > Hm, don't know about "most people".
> >
> > A Python 3.x programmer might think so.
> >
> > However, no competent C or C++ programmer assumes that.
>
> Right, competent programmers know that overflow in signed arithmetics is UB
> and do not rely on it in portable code. Even if it appears to work in some
> particular way now and is not trapped, nobody guarantees the same in the
> next version of gcc.

It's good that there's agreement about something, now. :-)

David Brown and I have already expressed, in this thread, about the same sentiment about preferably using unsigned for modular arithmetic.

Perhaps we have landed on that conclusion from slightly different rationales, but my rationale is that when it's not too much work to write standard-conforming code, then there is IMO no good reason to not do that.

However, the gcc default is that you can not rely on the expected behavior in other's code (other's code may not add the requisite casting to unsigned type, e.g. for use of Windows' GetTickCount API function), and the gcc default is that you cannot even rely on reasonable behavior for very system-specific code.

In short, it's unreliable. And due to the "optimizations" that it adds by default, that may wholesale remove parts of one's code, the effect of one's code (that may use others' libraries), with this compiler, is also unpredictable. Or perhaps that distinction is too subtle, too fine, but anyway, it's somewhere in the area of unreliable/unpredictable/arbitrary/impractical.

But as I've also already remarked on, "Better the Devil that one knows": with regard to conformance to the core language parts of the standard, and support for those parts of the standard, g++ shines brightly. :)

Tobias Müller

unread,
Oct 8, 2013, 3:27:29 PM10/8/13
to
<alf.p.s...@gmail.com> wrote:
> [...]
> On Tuesday, October 8, 2013 5:15:50 PM UTC+2, David Brown wrote:
>> Most people assume "int" works like a mathematical integer - not
>> modular arithmetic.
>
> Hm, don't know about "most people".
>
> A Python 3.x programmer might think so.
>
> However, no competent C or C++ programmer assumes that.
>
> Making that assumption might even be used as a defining criterion for
> incompetence and/or newbieness.

They way it is defined, it seems to be obvious that this was the original
intent. Why else should overflow be UB?
If you restrict yourself to a reasonable range you are on the safe side.

> Along with writing "void main", which you will find in examples all over
> Microsoft's documentation.

When did you read MSDN the last time? A quick google search (excluding C#)
revealed just one occurrence in an old Visual Studio 6.0 page.

>> If you have an integer "x" which you know is
>> non-negative, then you will assume that adding 1 to it will give you a
>> positive integer.
>
> No, all my reviews, when I worked (and now that I'm almost fully healed
> from surgeries I may just start working again, if employers are not
> scared away by long absence), said that I was extremely competent, not the opposite.
>
> So no, I would absolutely not make an assumption flagrantly in contradiction with reality.

I'm a bit baffled, that you (as an appearantly competent C++ programmer)
advocate for relying on clearly undefined behavior. The only sane thing to
do is make sure that overflow never happens!

>> That's the way maths works. Why shouldn't the
>> compiler make the same assumption?
>
> Because that's not the way that integer arithmetic works in C++, and
> because, far more importantly, because making and exploiting that
> assumption produces effectively incorrect machine code, even if formally allowed.
>
>
>> The only time the compiler will get it "wrong" (meaning "contrary to the
>> user's expectations" rather than "contrary to the standards") by
>> treating signed overflow as undefined behaviour is if you have code that
>> specifically relies on modular arithmetic of integers.
>
> Possibly right, but no point in debating that I think.
>
> Relying on modular arithmetic is pretty common.
>
> Because all integer arithmetic in C++ is now in practice modular (no
> Univac any more), and because for efficiency reasons trapping on integer
> overflow is in practice not used.

For the most signed integer types there isn't even a guaranteed size.
Without ugly preprocessor hackery there is no possibility to write portable
code that relies on modular integer arithmetic.
No, you cannot simply reverse that. The problem cases are only a (probably
small) subset of the optimization possibilities.

>> What would you rather do? Disable optimisations that conflict with
>> /your/ particular idea of "expected behaviour" regardless of the
>> standards?
>
> There are two incorrect assumptions in that question.
>
> The first incorrect assumption, that it's subjective to expect a now
> universal-for-C-and-C++ arithmetic behavior (not even gcc behaves
> differently unless one asks for trapping). It's not subjective. It's the reality.

It's not reality, because it's UB. Every C or C++ programmer should be
aware of that. If you know that it causes subtle bugs, why do you insist on
using it?

> The second incorrect assumption, that a compiler team is free to exploit
> formal loopholes that are in the standard in support of now archaic
> systems, regardless of practical consequences. The cost for something
> that occurs very seldom is low, but it is there. The waste of
> programmer's valuable time, and the mere EXISTENCE of such, which is
> noticed and remarked on, and even used to produce clever blog articles
> "guess what this code does" and the like, translates directly into a
> negative perception of the compiler.

The waste of programmers time is not the compilers fault, but the
programmers. You don't rely on modular arithmetic by _accident_!

>> Where do you draw the line? Should the compiler have a
>> "-fI-know-what-I-am-doing" flag?
>
> It should merely be PREDICTABLE, doing by default the most practical
> thing, as opposed to the most impractical, time-wasting and unimaginable thing.

It is predictable if you restrict yourself to the defined behavior.

> So it's an easy line to draw in most cases. ;-)

Yes, it's easy. Don't rely on UB.

> [...]

Tobi

Rupert Swarbrick

unread,
Oct 8, 2013, 3:43:30 PM10/8/13
to
junyangzou <zou...@gmail.com> writes:
>> If you don't know what an Abelian group is, how will it help you to
>> get answer about whether arithmetic forms such group?
>
> AFAIK, commutative and associative is a sufficient for an operation to
> form a Abelian group.

... and invertible.

A set with just a commutative and associative operation is called an
abelian monoid. One example of such an object is the free abelian monoid
on one generator. It is in bijection with the non-negative integers and
the operation is the same as addition.

Another way to think of it is as follows. I've got a "generating object"
X and want to be able to talk about X+X. Fine, but I should also be able
to talk about X+(X+X)=(X+X)+X (by associativity). And probably X+X+X+X
and... and... Let's abbreviate the sum of n X's as nX. I'm also going to
throw in another object, which I'll write as 0, and I'll say that
X+0=0+X=X. Notice that associativity now shows that nX+0 = 0+nX = nX, so
0 is a left and right unit. To make my notation nice and uniform, I
could define 0X = 0.

If I assume all of these objects are distinct, I get something that
looks awfully like the non-negative integers. Indeed, the bijection is
nX <-> n.

Note that I didn't mention subtraction (or negative numbers).

In a group, I also require that for any element, g, I can add something
to g to get back to zero. If I add a new element to my example above and
call it -X and ask that -X+X = X+(-X) = 0, then you see that
(-X)+(-X)+2X=0 and so on (by associativity again). Indeed, I may as well
write (-X)+(-X) as -2X. Now I've got an abelian group that's obviously
"the same" as the integers. (The technical term is isomorphic)

Hey, I didn't mention commutativity! Well, that's because it doesn't
matter in the example above, which is commutative by
definition. However, if I started out with two different objects (X and
Y, say), then I'd need to tell you whether or not you could assume
X+Y=Y+X.

Finally, back to my original point. Is signed addition invertible? Well,
that depends on what the standard says about behaviour on overflow. For
example, consider what happens if signed saturating addition is allowed.


Rupert

alf.p.s...@gmail.com

unread,
Oct 8, 2013, 3:49:08 PM10/8/13
to
On Tuesday, October 8, 2013 9:27:29 PM UTC+2, Tobias Müller wrote:
> <alf.p.s...@gmail.com> wrote:
>
> > [...]
> > On Tuesday, October 8, 2013 5:15:50 PM UTC+2, David Brown wrote:
> >> Most people assume "int" works like a mathematical integer - not
> >> modular arithmetic.
> >
> > Hm, don't know about "most people".
> >
> > A Python 3.x programmer might think so.
> >
> > However, no competent C or C++ programmer assumes that.
> >
> > Making that assumption might even be used as a defining criterion for
> > incompetence and/or newbieness.
>
> They way it is defined, it seems to be obvious that this was the original
> intent. Why else should overflow be UB?

C++ admits three possible representations of signed integers, namely magnitude-and-sign, one's complement, and two's complement. Only the last gives modulo arithmetic. At one time computers/systems using the first two, existed.

This is the main reason, and it pertains to now archaic systems.

But there is also a still-practically-possible reason, that of trapping on integer overflow. Even C has no direct interface for handling such traps, even though it's natural to think it will result in a "signal". In C++ the support for signals is, however, very much lacking, with almost all roads leading to UB.


> If you restrict yourself to a reasonable range you are on the safe side.

Yes, in general.


> > Along with writing "void main", which you will find in examples all over
> > Microsoft's documentation.
>
> When did you read MSDN the last time? A quick google search (excluding C#)
> revealed just one occurrence in an old Visual Studio 6.0 page.

You might google <<msdn c++ "void main">>.

I think your question was rhetoric, indicating that my statement was incorrect.

However, the inability to find all those examples is just about googling skills, or experience with MSDN. ;-)


[snip]
>
> I'm a bit baffled, that you (as an appearantly competent C++ programmer)
> advocate for relying on clearly undefined behavior.

Say, could you provide a quote of that?


[snip]

Sorry for not reading the rest, but, you know, time.

Tobias Müller

unread,
Oct 8, 2013, 4:50:06 PM10/8/13
to
<alf.p.s...@gmail.com> wrote:
> On Tuesday, October 8, 2013 9:27:29 PM UTC+2, Tobias Müller wrote:
>> <alf.p.s...@gmail.com> wrote:
>>
>>> [...]
>>> On Tuesday, October 8, 2013 5:15:50 PM UTC+2, David Brown wrote:
>>>> Most people assume "int" works like a mathematical integer - not
>>>> modular arithmetic.
>>>
>>> Hm, don't know about "most people".
>>>
>>> A Python 3.x programmer might think so.
>>>
>>> However, no competent C or C++ programmer assumes that.
>>>
>>> Making that assumption might even be used as a defining criterion for
>>> incompetence and/or newbieness.
>>
>> They way it is defined, it seems to be obvious that this was the original
>> intent. Why else should overflow be UB?
>
> C++ admits three possible representations of signed integers, namely
> magnitude-and-sign, one's complement, and two's complement. Only the last
> gives modulo arithmetic. At one time computers/systems using the first two, existed.
>
> This is the main reason, and it pertains to now archaic systems.

Modular overflow is just an implementation detail of twos complement and
just because twos complement made the cut does not mean that modular
overflow is more natural that any other behavior.

> But there is also a still-practically-possible reason, that of trapping
> on integer overflow. Even C has no direct interface for handling such
> traps, even though it's natural to think it will result in a "signal". In
> C++ the support for signals is, however, very much lacking, with almost
> all roads leading to UB.

In what way are signals more natural than exceptions?

Trapping overflow has actually no relevance here. It has an entirely
different purpose. It prevents _accidential_ overflow. But modular overflow
isn't any better than UB with respect to accidential overflow.

>> If you restrict yourself to a reasonable range you are on the safe side.
>
> Yes, in general.

Why "in general"? Except what?

>>> Along with writing "void main", which you will find in examples all over
>>> Microsoft's documentation.
>>
>> When did you read MSDN the last time? A quick google search (excluding C#)
>> revealed just one occurrence in an old Visual Studio 6.0 page.
>
> You might google <<msdn c++ "void main">>.

I just did and there was not even one example on the first 5 pages. I don't
have the time for looking through dozens of search result pages.

> I think your question was rhetoric, indicating that my statement was incorrect.

Yes. At least a massive overstatement.

> However, the inability to find all those examples is just about googling
> skills, or experience with MSDN. ;-)

Please give me some concrete examples. But not for ancient versions.

> [snip]
>>
>> I'm a bit baffled, that you (as an appearantly competent C++ programmer)
>> advocate for relying on clearly undefined behavior.
>
> Say, could you provide a quote of that?

I guess I've misunderstood you a bit here. But still, you are arguing to
make modular signed overflow (implementation) defined behavior, which is
IMO only slightly better.
Relying on modular overflow is essentially not possible (portably) without
also relying on other weak assumptions like the size of int.
And therefore legalizing it is a step into the wrong direction. It
encourages people to rely on it.

>
> [snip]
>
> Sorry for not reading the rest, but, you know, time.

This is just rude.
Considering your answers in other threads here you seem to have plenty of
time for answering much more "trolly" postings.

Tobi

Scott Lurndal

unread,
Oct 8, 2013, 4:54:53 PM10/8/13
to
alf.p.s...@gmail.com writes:
>On Tuesday, October 8, 2013 9:27:29 PM UTC+2, Tobias M=FCller wrote:
>> <alf.p.s...@gmail.com> wrote:

>
>C++ admits three possible representations of signed integers, namely magnit=
>ude-and-sign, one's complement, and two's complement. Only the last gives m=
>odulo arithmetic. At one time computers/systems using the first two, existe=
>d.

Computers using magnitude and sign still exist. The descendents of the
aforementioned Univac (known now as Clearpath Dorado) and the Burroughs
B5500 (known now as Clearpath Libra) still exist, are still being developed
and both host C and C++ compilers; and neither need use modulo arithmetic.

And, of course, one can't ignore the Z-series, for which new software is
developed every day (albeit mostly COBOL) and for which packed decimal is
a standard machine datatype.

Not to mention the decimal stuff added to C11.

alf.p.s...@gmail.com

unread,
Oct 8, 2013, 5:23:29 PM10/8/13
to
On Tuesday, October 8, 2013 10:54:53 PM UTC+2, Scott Lurndal wrote:
> alf.p.s...@gmail.com writes:
>
>
> Computers using magnitude and sign still exist. The descendents of the
> aforementioned Univac (known now as Clearpath Dorado) and the Burroughs
> B5500 (known now as Clearpath Libra) still exist, are still being developed
> and both host C and C++ compilers; and neither need use modulo arithmetic.

Even if 1 or 2 Dorados still exist in active service, they can for all practical purposes be ignored.

Just as the ENIAC. ;-)


> And, of course, one can't ignore the Z-series, for which new software is
> developed every day (albeit mostly COBOL) and for which packed decimal is
> a standard machine datatype.

Z-series, are you talking (before World War II) Konrad Zuse's Z1 etc. here?

Long memory then, if that.

Anyway, Konrad must be the most underrated man in the history of Computer Science, creating both the first digital computers and the first high level programming language (Plankakul), all before or during World War II, and losing all credit to John von Neumann, a Hungarian whose only contribution in this regard was to omit all references and credits in the internal but accidentally-widely-distributed memo that he wrote right after calculating the optimal height to explode an atomic bomb over Hiroshima.


> Not to mention the decimal stuff added to C11.

There are no decimal integer types in C++.

On the contrary, the standard has always included a requirement of pure binary,

in C++11 §3.9.1/7 "The representations of integral types shall define values by use of a pure binary numeration system."


Cheers & hth.,

- Alf (detecting a bit of trolling here)

Ian Collins

unread,
Oct 8, 2013, 5:27:02 PM10/8/13
to
alf.p.s...@gmail.com wrote:
> On Tuesday, October 8, 2013 10:54:53 PM UTC+2, Scott Lurndal wrote:
>
>> And, of course, one can't ignore the Z-series, for which new software is
>> developed every day (albeit mostly COBOL) and for which packed decimal is
>> a standard machine datatype.
>
> Z-series, are you talking (before World War II) Konrad Zuse's Z1 etc. here?

You're several decades behind the times Alf!

http://en.wikipedia.org/wiki/IBM_System_z


--
Ian Collins

alf.p.s...@gmail.com

unread,
Oct 8, 2013, 5:29:49 PM10/8/13
to
On Tuesday, October 8, 2013 10:50:06 PM UTC+2, Tobias Müller wrote:
> <alf.p.s...@gmail.com> wrote:
>
[snip]

> > You might google <<msdn c++ "void main">>.
>
> I just did and there was not even one example on the first 5 pages. I don't
> have the time for looking through dozens of search result pages.
>
> Please give me some concrete examples. But not for ancient versions.

http://msdn.microsoft.com/en-us/library/windows/desktop/bb773687%28v=vs.85%29.aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/bb773745%28v=vs.85%29.aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/bb773757%28v=vs.85%29.aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/bb773739%28v=vs.85%29.aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/bb773742%28v=vs.85%29.aspx

http://msdn.microsoft.com/en-us/library/k204dhw5.aspx

http://msdn.microsoft.com/en-us/library/aa448695.aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/aa446602%28v=vs.85%29.aspx

I'm stopping there, not for me to do Microsoft's cleaning job for them.

But as you can see, no shortage of "void main" usage in their docs, indicating a certain lack of competence at least on the part of the technical writers.

You're just wrong.


> > Sorry for not reading the rest, but, you know, time.
>
> This is just rude.

No seriously, I don't have time for reading pages of stuff after encountering active misrepresentation.

David Brown

unread,
Oct 8, 2013, 6:22:38 PM10/8/13
to
On 08/10/13 20:50, alf.p.s...@gmail.com wrote:
> On Tuesday, October 8, 2013 6:55:04 PM UTC+2, Paavo Helde wrote:
>> alf.p.s...@gmail.com wrote in
>> news:881daf99-67bf-4256...@googlegroups.com:
>>> On Tuesday, October 8, 2013 5:15:50 PM UTC+2, David Brown wrote:
>>>> On 08/10/13 15:22, alf.p.s...@gmail.com wrote:
>>
>>>> Most people assume "int" works like a mathematical integer -
>>>> not modular arithmetic.
>>
>>> Hm, don't know about "most people".
>>>
>>> A Python 3.x programmer might think so.
>>>
>>> However, no competent C or C++ programmer assumes that.
>>
>> Right, competent programmers know that overflow in signed
>> arithmetics is UB and do not rely on it in portable code. Even if
>> it appears to work in some particular way now and is not trapped,
>> nobody guarantees the same in the next version of gcc.
>
> It's good that there's agreement about something, now. :-)

If you are agree that competent programmers know that signed overflow is
undefined behaviour, why are you arguing that compilers should define it
in a specific way?

>
> David Brown and I have already expressed, in this thread, about the
> same sentiment about preferably using unsigned for modular
> arithmetic.
>
> Perhaps we have landed on that conclusion from slightly different
> rationales, but my rationale is that when it's not too much work to
> write standard-conforming code, then there is IMO no good reason to
> not do that.

So if your code is standard-conforming, you assume nothing about signed
overflow. Again, why are you complaining that a compiler agrees with you?

>
> However, the gcc default is that you can not rely on the expected
> behavior in other's code (other's code may not add the requisite
> casting to unsigned type, e.g. for use of Windows' GetTickCount API
> function), and the gcc default is that you cannot even rely on
> reasonable behavior for very system-specific code.

The gcc default is for no optimisation, thus no problem. It's default
treatment of optimising signed overflow is to accept that it is
undefined behaviour, and assume that the programmer does not make
mistakes leading to signed overflow. As far as I can tell, we already
agree that competent programmers will be happy here, as they know that
signed overflow is undefined, and they know how to use unsigned integers
when they need modular arithmetic overflow behaviour.

So maybe our disagreement is just about incompetent programmers. You
think incompetent programmers will assume signed integers are modular,
and therefore this should be the default. I think incompetent
programmers will assume that signed integers act like real-world
integers, and therefore undefined behaviour on overflow is fine (since
the compiler can't do any better).

Is that a fair summary of the argument?

David Brown

unread,
Oct 8, 2013, 6:25:05 PM10/8/13
to
On 08/10/13 18:22, alf.p.s...@gmail.com wrote:

> Along with writing "void main", which you will find in examples all over Microsoft's documentation.
>

"void main" is perfectly legal in freestanding (non-hosted) C, and is in
common usage in embedded systems of all sorts.

I can't say anything about MS's toolchains or documentation, as I've
never used them. But I agree it is an incorrect declaration of main()
in a hosted system.


alf.p.s...@gmail.com

unread,
Oct 8, 2013, 6:41:25 PM10/8/13
to
On Wednesday, October 9, 2013 12:25:05 AM UTC+2, David Brown wrote:
> On 08/10/13 18:22, alf.p.s...@gmail.com wrote:
>
> > Along with writing "void main", which you will find in examples all over Microsoft's documentation.
>
> "void main" is perfectly legal in freestanding (non-hosted) C, and is in
> common usage in embedded systems of all sorts.

Well we're talking C++ here.

In C++ it's invalid, even in freestanding implementation.

C++11 §3.6.1/2 "[main] shall have a return type of type int, but otherwise its type is implementation-defined"

I don't know about C.

alf.p.s...@gmail.com

unread,
Oct 8, 2013, 6:49:44 PM10/8/13
to
On Wednesday, October 9, 2013 12:22:38 AM UTC+2, David Brown wrote:
>
> Is that a fair summary of the argument?
>

Nope.

A compiler that yields unpredictable (lack of) effects for ordinary code, is simply ... unpredictable.

Not a good thing, regardless of the users.

And let's face: not every C++ programmer is a language lawyer.

A compiler, such as g++, should not support only the best and brightest, but also ordinary programmers.

And the compiler should not only support the programmers who think those who use platform-specific functionality are incompetents.

The compiler should also support those, like myself and (I know from earlier clc++ discussions) Pete Becker, who penned the latest standard, who are comfortable with using platform-specific functionality where appropriate, regardless of formal UB. Not that he'd necessary completely share my point of view (I think most probably not!), but as authority arguments go, I think it's nice that by chance he has expressed, in clc++, views diametrically opposed to yours, in this matter.

alf.p.s...@gmail.com

unread,
Oct 8, 2013, 7:55:48 PM10/8/13
to
On Tuesday, October 8, 2013 11:27:02 PM UTC+2, Ian Collins wrote:
> alf.p.s...@gmail.com wrote:
>
> > Z-series, are you talking (before World War II) Konrad Zuse's Z1 etc. here?
>
> You're several decades behind the times Alf!

He he. :)


> http://en.wikipedia.org/wiki/IBM_System_z

Thanks.

I see no evidence or indication that it uses anything but two's complement for integers, but knowing IBM, it might just do that plus having a Hollerith card based system console and using EBCDIC through and through. Of course with a special Java adapter somewhere, so that Java can run there. Ow hell. :(


Cheers, & thanks!,

- Alf

Paavo Helde

unread,
Oct 9, 2013, 12:53:28 AM10/9/13
to
Rupert Swarbrick <rswar...@gmail.com> wrote in news:l31n8t$34i$2
@speranza.aioe.org:
>
> Finally, back to my original point. Is signed addition invertible? Well,
> that depends on what the standard says about behaviour on overflow. For
> example, consider what happens if signed saturating addition is allowed.

C++ standard is very clear here, overflow in signed arithmetics is
undefined behaviour. So certainly it is not guaranteed to be invertible.

Cheers
Paavo

David Brown

unread,
Oct 9, 2013, 7:29:56 AM10/9/13
to
On 09/10/13 00:49, alf.p.s...@gmail.com wrote:
> On Wednesday, October 9, 2013 12:22:38 AM UTC+2, David Brown wrote:
>>
>> Is that a fair summary of the argument?
>>
>
> Nope.
>
> A compiler that yields unpredictable (lack of) effects for ordinary
> code, is simply ... unpredictable.
>
> Not a good thing, regardless of the users.
>
> And let's face: not every C++ programmer is a language lawyer.
>

A C or C++ programmer who writes code without a defined meaning, but
expects the compiler to read his mind for what he wants, has a lot to
learn. I don't want my tools to be limited to deal with that level of
ignorance. (I mean no disrespect to such programmers - you've got to
start somewhere. But if you don't understand the need to write correct
code according to the language, you have a long way yet to go.)


> A compiler, such as g++, should not support only the best and
> brightest, but also ordinary programmers.

We are back to (almost) my summary. All I need to do is change the word
"incompetent" to "ordinary" :

You think ordinary programmers will assume signed integers are modular,
and therefore this should be the default. I think ordinary programmers
will assume that signed integers act like real-world integers, and
therefore undefined behaviour on overflow is fine (since the compiler
can't do any better).

If you (or any other programmer - ordinary, incompetent, wizard, or
whatever) write code that has the possibility of integer overflow then
there is no way the compiler can generate code that is "correct",
because there is no meaning of "correct" here.

To one programmer, "correct" might mean "as modular arithmetic,
overflowing from +BIG to -BIG". To another it might mean "as modular
arithmetic, but obviously not changing the sign - +BIG overflows to 0".
Another will want saturation - "+BIG overflows to +BIG". Another will
want a trap, or exception. Many will want magic - "+BIG overflows to
+EVEN_BIGGER". And some will want "whatever makes the smallest and
fastest code, because the answer doesn't matter".


You cannot simply pick one of these and say "this is what ordinary
programmers expect" - just because it happens to coincide with what is
usually the easiest machine code. Consistency is good, but consistently
wrong is not significantly better than inconsistently wrong.


The only conceivable "correct" implementations of signed overflow is for
the compiler to give a warning about it (which gcc will do, when it can
spot such problems at compile time, if suitable warnings are enabled) or
for the compiler to generate run-time range checking code. Such code is
expensive, but sometimes useful and supported by some languages (such as
Ada).

>
> And the compiler should not only support the programmers who think
> those who use platform-specific functionality are incompetents.

I don't think those who use platform-specific functionality are
incompetent - as an embedded programmer, I use platform-specific
functionality all the time (and I view myself as "competent" :-) But I
do think that people who rely unnecessarily on /clearly/ undefined
behaviour /are/ incompetent - or at least ignorant. It is particularly
true with gcc, since if you actually want to rely on the effects of this
particular undefined behaviour you can use compiler switches to make it
defined - and therefore usable.

>
> The compiler should also support those, like myself and (I know from
> earlier clc++ discussions) Pete Becker, who penned the latest
> standard, who are comfortable with using platform-specific
> functionality where appropriate, regardless of formal UB. Not that
> he'd necessary completely share my point of view (I think most
> probably not!), but as authority arguments go, I think it's nice that
> by chance he has expressed, in clc++, views diametrically opposed to
> yours, in this matter.
>

No, this sounds like he expresses views exactly like mine regarding
platform-specific behaviour. I have no problem with using
platform-specific behaviour where it helps the code - usually code will
only ever run on one or a few platform types. If you want modular
effects on signed overflow, then stick to a platform that supports it -
such as gcc with the "-fwrapv" flag. And avoid platforms that don't
support it - that is every single C compiler in existence that does not
explicitly state that it has this behaviour. The worst thing you can
possibly do is have a couple of tests on a compiler and /assume/ it
defines the undefined behaviour in a way you /assume/ makes sense.

alf.p.s...@gmail.com

unread,
Oct 9, 2013, 7:50:34 AM10/9/13
to
On Wednesday, October 9, 2013 1:29:56 PM UTC+2, David Brown wrote:
> On 09/10/13 00:49, alf.p.s...@gmail.com wrote:
> > A compiler that yields unpredictable (lack of) effects for ordinary
> > code, is simply ... unpredictable.
> >
> > Not a good thing, regardless of the users.
> >
> > And let's face: not every C++ programmer is a language lawyer.
>
> A C or C++ programmer who writes code without a defined meaning, but
> expects the compiler to read his mind for what he wants, has a lot to
> learn.

That's totally irrelevant, a slur.

Do quote the alleged "code without a defined meaning".


[snipped pages of further misrepresentation and innuendo]

- Alf (annoyed)

David Brown

unread,
Oct 9, 2013, 9:39:36 AM10/9/13
to
My post was not meant to be a slur or to cause annoyance of any sort. I
am sorry you took it that way.

But like it or not, a lot of C and C++ behaviour has clear definitions,
and there is a lot that is legal to write in C or C++ but is documented
as "undefined behaviour". That is the way the language works - and no
amount of discussions of "what programmers expect" will change that.

Clearly people who are relatively new to the language will not be aware
of such situations - it is a learning process. Thus, people who write
code that relies on undefined behaviour have a lot to learn. That is
not a "slur", it is simple fact.

If you /know/ that such code has undefined behaviour (and are not able
to pin it down to "defined" by a choice of compiler and/or options, thus
becoming entirely usable "implementation defined behaviour"), and
/still/ write code that relies on it, then you are expecting the
compiler to read your mind and agree with you on how you think this
behaviour /should/ be defined. This is always wrong.

Again, I don't see how you interpret that as a slur.


mvh.,

David (confused about your annoyance)

David Brown

unread,
Oct 9, 2013, 9:50:44 AM10/9/13
to
On 08/10/13 21:49, alf.p.s...@gmail.com wrote:
> On Tuesday, October 8, 2013 9:27:29 PM UTC+2, Tobias Müller wrote:
>> <alf.p.s...@gmail.com> wrote:
>>
>>> [...] On Tuesday, October 8, 2013 5:15:50 PM UTC+2, David Brown
>>> wrote:
>>>> Most people assume "int" works like a mathematical integer -
>>>> not modular arithmetic.
>>>
>>> Hm, don't know about "most people".
>>>
>>> A Python 3.x programmer might think so.
>>>
>>> However, no competent C or C++ programmer assumes that.
>>>
>>> Making that assumption might even be used as a defining criterion
>>> for incompetence and/or newbieness.
>>
>> They way it is defined, it seems to be obvious that this was the
>> original intent. Why else should overflow be UB?
>
> C++ admits three possible representations of signed integers, namely
> magnitude-and-sign, one's complement, and two's complement. Only the
> last gives modulo arithmetic. At one time computers/systems using the
> first two, existed.
>
> This is the main reason, and it pertains to now archaic systems.
>

If the early creators of C (since this pre-dates and is inherited by
C++) and its standards thought that defining signed overflow as modular
behaviour were important, they would have defined it that way. At the
very least, they would have made it "implementation defined" rather than
"undefined".

But they specifically made it "undefined". This is not just for the
convenience of implementation on now-outdated computers. It is because
they did not think modular arithmetic was a sensible or obvious model
for signed computation - or at least, it is not the /only/ sensible
choice. Rather than picking one model, and forcing implementers and
users to follow it, they said that /no/ model of signed overflow is
considered correct in C.

Other languages have made different design decisions - in Java, signed
overflow is modular. In Python, attempting signed overflow results in
the type getting bigger to avoid overflow. You cannot say that one of
these is "correct".

The fact that the undefined behaviour of signed overflow in C can give
extra optimisation opportunities is a bonus - I doubt if the
grandfathers of C thought that far ahead.

David Brown

unread,
Oct 9, 2013, 9:53:06 AM10/9/13
to
I've only looked at the details for C - I didn't know it was invalid in
freestanding C++ programs. Of course, "void main(void)" is still likely
to be used in embedded C++ code, but it's nice to know that it should
not be used.

alf.p.s...@gmail.com

unread,
Oct 9, 2013, 9:56:19 AM10/9/13
to
On Wednesday, October 9, 2013 3:39:36 PM UTC+2, David Brown wrote:
> On 09/10/13 13:50, alf.p.s...@gmail.com wrote:
>
> > On Wednesday, October 9, 2013 1:29:56 PM UTC+2, David Brown wrote:
>
> >> On 09/10/13 00:49, alf.p.s...@gmail.com wrote:
>
> >>> A compiler that yields unpredictable (lack of) effects for ordinary
> >>> code, is simply ... unpredictable.
>
> >>> Not a good thing, regardless of the users.
> >>>
> >>> And let's face: not every C++ programmer is a language lawyer.
> >>
> >> A C or C++ programmer who writes code without a defined meaning, but
> >> expects the compiler to read his mind for what he wants, has a lot to
> >> learn.
> >
> > That's totally irrelevant, a slur.
> > Do quote the alleged "code without a defined meaning".
> >
> > [snipped pages of further misrepresentation and innuendo]
>
> My post was not meant to be a slur or to cause annoyance of any sort. I
> am sorry you took it that way.

No quote of the alleged "code without a defined meaning".

Since there was no such.

I.e. your claim of being sorry can only refer to being caught.


> But like it or not, a lot of C and C++ behaviour has clear definitions,
> and there is a lot that is legal to write in C or C++ but is documented
> as "undefined behaviour". That is the way the language works - and no
> amount of discussions of "what programmers expect" will change that.

This pretends to be arguing against something I have written.

It is at best a misrepresentation.


- Alf

David Brown

unread,
Oct 9, 2013, 10:16:10 AM10/9/13
to
On 09/10/13 15:56, alf.p.s...@gmail.com wrote:
> On Wednesday, October 9, 2013 3:39:36 PM UTC+2, David Brown wrote:
>> On 09/10/13 13:50, alf.p.s...@gmail.com wrote:
>>
>>> On Wednesday, October 9, 2013 1:29:56 PM UTC+2, David Brown wrote:
>>
>>>> On 09/10/13 00:49, alf.p.s...@gmail.com wrote:
>>
>>>>> A compiler that yields unpredictable (lack of) effects for ordinary
>>>>> code, is simply ... unpredictable.
>>
>>>>> Not a good thing, regardless of the users.
>>>>>
>>>>> And let's face: not every C++ programmer is a language lawyer.
>>>>
>>>> A C or C++ programmer who writes code without a defined meaning, but
>>>> expects the compiler to read his mind for what he wants, has a lot to
>>>> learn.
>>>
>>> That's totally irrelevant, a slur.
>>> Do quote the alleged "code without a defined meaning".
>>>
>>> [snipped pages of further misrepresentation and innuendo]
>>
>> My post was not meant to be a slur or to cause annoyance of any sort. I
>> am sorry you took it that way.
>
> No quote of the alleged "code without a defined meaning".
>
> Since there was no such.
>
> I.e. your claim of being sorry can only refer to being caught.

I am still lost here.

I said that a programmer who writes code without a defined meaning has a
lot to learn. At no point did I say /you/ wrote such code, or that
/you/ "have a lot to learn". I certainly can't quote code that has not
been posted, but you certainly cannot demand a quotation to defend
something I did not say.

Again, I am sorry you have misunderstood what I wrote, and taken offence
at inferred slurs. But I cannot see anything I wrote that directly or
indirectly implied a slur.

The only thing I can think of here is that you /have/ written code that
depends on modular behaviour of signed overflow, without taking suitable
precautions (such as using "-fwrapv" with gcc, or checking the
documentation of other compilers you expect to use). If that's the
case, then yes - you have written code without a defined meaning, and
that is therefore bad code. I don't mean that to be offensive, but as
constructive criticism to help you improve your programming. But I
can't quote you on anything here, as this is just a guess - only /you/
can confirm or deny that.

>
>
>> But like it or not, a lot of C and C++ behaviour has clear definitions,
>> and there is a lot that is legal to write in C or C++ but is documented
>> as "undefined behaviour". That is the way the language works - and no
>> amount of discussions of "what programmers expect" will change that.
>
> This pretends to be arguing against something I have written.
>
> It is at best a misrepresentation.
>

It is either something you agree with, in which case write "Yes", or it
is something you /disagree/ with - in which case try to explain why.


Alf P. Steinbach

unread,
Oct 9, 2013, 12:30:36 PM10/9/13
to
On 09.10.2013 15:50, David Brown wrote:
>>>
>>>> [...] On Tuesday, October 8, 2013 5:15:50 PM UTC+2, David Brown
>>>> wrote:
>>>>> Most people assume "int" works like a mathematical integer -
>>>>> not modular arithmetic.
>
> If the early creators of C (since this pre-dates and is inherited by
> C++) and its standards thought that defining signed overflow as modular
> behaviour were important, they would have defined it that way.

Who knows. If they had thought 'bool' or 'void' were important, they
would have included those types. If they thought humans were meant to
fly in the air, they would have equipped us with gills. Oh, wait... !

And just as the argument about what the early C creators would have done
and their motivations, is pure nonsense, so is the implied relevance to
anything earlier in this thread.

I.e., the above speculation about motivations etc. it's pure nonsense
supposition which in addition is about an irrelevance.


> At the
> very least, they would have made it "implementation defined" rather than
> "undefined".

The first C standard was C89.

I'm pretty sure the "early creators" were engaged in that process, but
it was by committee, and it defined a language quite different from early C.

C was created in the middle 1970's, with early starts made already
pre-1970, IIRC.

At the time there was no language standard, but later the language was
effectively defined by the book "The C Programming Language" by
Kernighan and Ritchie (my first edition of that book rests comfortably
in a box in the outhouse), which is why it's referred to as "K&R C".

K&R C, early C, was a language quite different from modern C. You can
see this language used for actual large scale programming in not-so-old
source code for gcc. E.g., function declarations differ, and much else.

When you conflate that with C89 and later, and conflate THAT again with
C++, and combine that soup of conflations with speculations about
possible motivations for decision about irrelevant stuff in the 1970s,
then that is ...

well it can be misleading to some readers, I guess, so it can serve that
purpose, but other than that it's just pure meaningless nonsense,
balderdash.


[even more silly nonsense speculations elided]

Rupert Swarbrick

unread,
Oct 9, 2013, 2:08:04 PM10/9/13
to
Yep. I was going for the Socratic question. You probably don't have to
prove to me that *you* understand the C++ standard (whichever revision).

Rupert

Alf P. Steinbach

unread,
Oct 9, 2013, 4:02:43 PM10/9/13
to
That sounds a bit far fetched, like a thief caught red-handed with the
house's silver cutlery and denying that he was at all intending to take
it out of the house.

But it happens I have occasionally written code with UB (apparently you
don't have enough experience to have done that), and moreover I do have
a lot to learn, and I hope I will continue to learn for a long time...

However, no matter whether I'm dumb or smart, have lots to learn or not,
that does not help the reputation of gcc as being rather perverse and
impractical in its exploitation of formal loop-holes.

And using such sly arguments certainly does not help your reputation.



> I certainly can't quote code that has not been posted

Right.


[snip]
>>
>>> But like it or not, a lot of C and C++ behaviour has clear definitions,
>>> and there is a lot that is legal to write in C or C++ but is documented
>>> as "undefined behaviour". That is the way the language works - and no
>>> amount of discussions of "what programmers expect" will change that.
>>
>> This pretends to be arguing against something I have written.
>>
>> It is at best a misrepresentation.
>
> It is either something you agree with, in which case write "Yes", or it
> is something you /disagree/ with - in which case try to explain why.

The context indicates that your explanation (?) addresses something I
have written -- that it somehow corrects or teaches.

In that most natural interpretation, as correction or teaching, it's
false, a statement that only misleads.

I have not written anything that could make you doubt I'm unaware of the
standards.

On the contrary, in my answer to the OP, first in this thread, I
referred to "the formal UB for overflow" [of signed arithmetic].

You replied to that.

Therefore, you can't be unaware that I'm well aware of the C++ standard
(and elsewhere in this thread it's shown that you aren't, with respect
to the requirements on "main", but that you do know a bit about the C
standard, which I happily learned from). And so the statement is an
attempt to make others believe something that you /know/ is false.

It also has an interpretation where it's literally true but meaningless,
like a tautology (did you know, a definition is a definition), but I
think that no-one of sound mind could offer that as argument for or
against anything.

In short, like the first comment that you now say was intended to just
point out the existence of programmers and that every programmer has a
lot to learn, the second comment, which you now say was just intended to
point out the existence of standards, is nothing but deception.


- Alf

David Brown

unread,
Oct 9, 2013, 4:47:29 PM10/9/13
to
Oh, for goodness sake, get over yourself! Are you so paranoid that you
really think I am finding subtle ways to insult you? If I wanted to
insult you - and believe me, I'm getting very tempted now - I would do so.

>
> But it happens I have occasionally written code with UB (apparently you
> don't have enough experience to have done that), and moreover I do have
> a lot to learn, and I hope I will continue to learn for a long time...
>

I have written plenty of code with undefined behaviour - but not
knowingly. I too have learned over the years, and intend to continue
doing so. (Some has had undefined behaviour according to the standards,
but was defined on the platform I was using - much like using "-fwrapv".)

> However, no matter whether I'm dumb or smart, have lots to learn or not,
> that does not help the reputation of gcc as being rather perverse and
> impractical in its exploitation of formal loop-holes.
>

I haven't heard such a "reputation" of gcc except from you, and I have
seen /nothing/ in your posts to justify such claims. Sure, gcc has its
faults and limitations, just like any other toolchain - and I've heard
people complain about it in particular cases (such as for IEEE-accurate
floating point work).

> And using such sly arguments certainly does not help your reputation.
>

"Sly arguments" ?

>
>> I certainly can't quote code that has not been posted
>
> Right.

And you can't jump over the moon. That makes us equal in the number of
irrelevant and impossible things we can't do.

Once you can pull a quotation out of the air to justify thinking that I
accused /you/ of knowingly and incompetently writing code with undefined
behaviour, then I am sure I can provide an equally imaginary quotation
justifying it. Until then, if you want to pick a fight then go
somewhere else.

>
>
> [snip]
>>>
>>>> But like it or not, a lot of C and C++ behaviour has clear definitions,
>>>> and there is a lot that is legal to write in C or C++ but is documented
>>>> as "undefined behaviour". That is the way the language works - and no
>>>> amount of discussions of "what programmers expect" will change that.
>>>
>>> This pretends to be arguing against something I have written.
>>>
>>> It is at best a misrepresentation.
>>
>> It is either something you agree with, in which case write "Yes", or it
>> is something you /disagree/ with - in which case try to explain why.
>
> The context indicates that your explanation (?) addresses something I
> have written -- that it somehow corrects or teaches.
>

No, it is a clarification of an important point. There is no
implication that you don't know the point already - but there /is/ an
implication that you don't think it is important. That's why I
emphasised it.

> In that most natural interpretation, as correction or teaching, it's
> false, a statement that only misleads.

It is a reminder of what you already know. You can accept it, or
challenge it, or perhaps say that it is not as important as your own
assumptions of how compilers should behave regardless of the standards,
or say that you understand it but think the standards are poor. I
suppose you have the right to assume it is an insult or slur, if you
really want to, but don't expect me to understand that interpretation.

>
> I have not written anything that could make you doubt I'm unaware of the
> standards.

I agree with that statement.

>
> On the contrary, in my answer to the OP, first in this thread, I
> referred to "the formal UB for overflow" [of signed arithmetic].
>
> You replied to that.
>
> Therefore, you can't be unaware that I'm well aware of the C++ standard
> (and elsewhere in this thread it's shown that you aren't, with respect
> to the requirements on "main", but that you do know a bit about the C
> standard, which I happily learned from).

Few people know /all/ the details of /all/ the C and C++ standards - I
am happy to be corrected on minor details, but I believe I understand
the major points that affect my work.

> And so the statement is an
> attempt to make others believe something that you /know/ is false.

You seem to have wandered down a very convoluted path to get to some
idea that I am spreading vicious rumours that you don't know your C or
C++ standards.

I know fine that you understand that signed overflow is undefined
behaviour - that has never been questioned. What I /do/ question is
your belief that it is better to define it as modular arithmetic, and
that gcc is uniquely and intentionally perverse in not following that idea.

David Brown

unread,
Oct 9, 2013, 4:48:41 PM10/9/13
to
<snipping the extras, and skipping to the relevant point>

Alf P. Steinbach

unread,
Oct 9, 2013, 6:15:15 PM10/9/13
to
On 09.10.2013 22:48, David Brown wrote:
> <snipping the extras, and skipping to the relevant point>
>
> I know fine that you understand that signed overflow is undefined
> behaviour - that has never been questioned.

Thanks.


> What I /do/ question is your belief that it is better to define it as
> modular arithmetic, and that gcc is uniquely and intentionally perverse
> in not following that idea.

Well those are two separate issues.


(1)
Regarding the first issue, it's this: is it as of 2013 advantageous in
some way to DEFINE C++ signed arithmetic as modulo arithmetic?

In my opinion yes. I wrote thusly in this group about that in 2004,
https://groups.google.com/d/msg/comp.lang.c++/E6xhw_YN97c/xDuAa5AXbkMJ

[quote]
The current UB prevents programs from checking for error and from
utilizing the overflow behavior, in a formally portable manner.

That's far worse and far more concrete than the extremely marginal and
hypothetical possibility of some error-detecting implementation, which
AFAIK does not exist (if it had some utility, presumably it would exist).

So the argument about allowing an implementation to treat overflow as
error is a fallacy. :-)

On the other hand, the thread in [comp.std.c++] showed that the
arguments in favor of standardization are not especially forceful.

And there the matter rests, I think (personally I agree with John, but
nice-to-have is not need-to-have, and standardizing existing practice
would be a break with the tradition in C++ standardization, wouldn't it?).
[/quote]

My reference to lack of [reliable] error checking implementation appears
to now have been voided by the clang compiler (see
http://lists.cs.uiuc.edu/pipermail/cfe-dev/2010-September/010906.html).

Also, the wording "prevents" was and is too strong, except in a
practical sense, which I probably was just assuming. Not having formally
defined modular arithmetic merely makes checking more impractical in
single-source portable code. But that's bad enough.

So I think that that 2004 argument -- for a change in the language
definition, reflecting the now universal practice -- is still
forceful, now 9 years later, for it would replace "unpredictable" with
"reliable", which in turn, of course, is exploitable. :)

And in addition there is the issue of disallowing counter-intuitive and
meaning-changing optimizations.


(2)
Regarding "that gcc is uniquely and intentionally perverse in not
following that idea",

NO, there is no data that I'm aware of to say that the resulting
perverseness is intentional, and

NO, the perverseness does not result from not following the idea of
changing the language rules.

Rather, as already explained, the perverseness follows from exploiting
formal UB to do the unexpected, as a kind of micro-optimization that
indicates that the programmers disregarded the effect on more important
global issues such as predictability, employing a very narrow view.

It's as if the compiler replaced "x = foo( i++, i++ )" with "x = 666;",
with no call of foo(), because that optimization -- yes it's FASTER --
is allowed by the formal UB, and if the missing call is undesired then
it's the programmer's own fault surely -- surely the programmer
intended to have the compiler "optimize" the specified call, yes?

One simply does not expect a compiler to be that UNREASONABLE. One
expects it to be reasonable and predictable. With perhaps some option
for doing very aggressive things when the need for that is great.

- Alf

David Brown

unread,
Oct 9, 2013, 7:11:47 PM10/9/13
to
On 10/10/13 00:15, Alf P. Steinbach wrote:
> On 09.10.2013 22:48, David Brown wrote:
>> <snipping the extras, and skipping to the relevant point>
>>
>> I know fine that you understand that signed overflow is undefined
>> behaviour - that has never been questioned.
>
> Thanks.
>
>
>> What I /do/ question is your belief that it is better to define it as
>> modular arithmetic, and that gcc is uniquely and intentionally perverse
>> in not following that idea.
>
> Well those are two separate issues.
>
>
> (1)
> Regarding the first issue, it's this: is it as of 2013 advantageous in
> some way to DEFINE C++ signed arithmetic as modulo arithmetic?
>
> In my opinion yes. I wrote thusly in this group about that in 2004,
> https://groups.google.com/d/msg/comp.lang.c++/E6xhw_YN97c/xDuAa5AXbkMJ

(I've just read your quotation below, rather than the referenced thread.)

>
> [quote]
> The current UB prevents programs from checking for error and from
> utilizing the overflow behavior, in a formally portable manner.
>

You are thinking here of something like this:

void foo(int a, int b) {
if ((a >= 0) && (b >= 0)) {
int c = a + b;
if (c < 0) {
// Error detected!

That will work if and only if signed overflow has modular semantics.



You can do the same thing in portable standards-compliant code:

void foo(int a, int b) {
if ((a >= 0) && (b >= 0)) {
unsigned int c = (unsigned int) a + (unsigned int) b;
if (c >= INT_MAX) {
// Error detected!


In general, my preference is to check for errors in parameters in
advance (or simply never call my functions with incorrect parameters -
that's the joy of writing most of my code on my own), rather than trying
the code then checking for errors afterwards.



> That's far worse and far more concrete than the extremely marginal and
> hypothetical possibility of some error-detecting implementation, which
> AFAIK does not exist (if it had some utility, presumably it would exist).
>

Such error-detecting capabilities /do/ exist, but only if the compiler
can figure them out at compile time.


> So the argument about allowing an implementation to treat overflow as
> error is a fallacy. :-)
>
> On the other hand, the thread in [comp.std.c++] showed that the
> arguments in favor of standardization are not especially forceful.
>
> And there the matter rests, I think (personally I agree with John, but
> nice-to-have is not need-to-have, and standardizing existing practice
> would be a break with the tradition in C++ standardization, wouldn't it?).
> [/quote]
>
> My reference to lack of [reliable] error checking implementation appears
> to now have been voided by the clang compiler (see
> http://lists.cs.uiuc.edu/pipermail/cfe-dev/2010-September/010906.html).
>
> Also, the wording "prevents" was and is too strong, except in a
> practical sense, which I probably was just assuming. Not having formally
> defined modular arithmetic merely makes checking more impractical in
> single-source portable code. But that's bad enough.
>
> So I think that that 2004 argument -- for a change in the language
> definition, reflecting the now universal practice -- is still
> forceful, now 9 years later, for it would replace "unpredictable" with
> "reliable", which in turn, of course, is exploitable. :)
>

I think you are wrong to talk about "universal practice" here. The
machines used for C and C++ will (almost) universally use two's
compliment signed integers, but it is certainly not "universal practice"
amongst programmers to assume modular behaviour of signed overflow. The
nearest to "universal practice" is to close your eyes and assume that it
won't happen.

> And in addition there is the issue of disallowing counter-intuitive and
> meaning-changing optimizations.

Again, I disagree with that description. I think modular behaviour of
signed overflow is counter-intuitive, and since I treat signed overflow
as "undefined behaviour" without expecting anything else, any
optimisations here do not change the meaning.

>
>
> (2)
> Regarding "that gcc is uniquely and intentionally perverse in not
> following that idea",
>
> NO, there is no data that I'm aware of to say that the resulting
> perverseness is intentional, and
>

That's how I read your comments, but fair enough.

> NO, the perverseness does not result from not following the idea of
> changing the language rules.
>
> Rather, as already explained, the perverseness follows from exploiting
> formal UB to do the unexpected, as a kind of micro-optimization that
> indicates that the programmers disregarded the effect on more important
> global issues such as predictability, employing a very narrow view.

I think we strongly disagree on how "unexpected" this undefined
behaviour is. To me, it is perfectly natural - even though I live in a
world of bits and bytes and know how the signed addition is implemented
down to the gate and register level, I don't think it makes sense to
view signed integers as modular. That's one of the reasons for using
unsigned integers (which I use more than signed ones). I also disagree
with the complaints of "micro-optimisation" - in my world, optimisations
are vital, and I don't want my compiler to waste instructions unnecessarily.

I have read through the gcc manual a fair number of times over the
years. I pick options that make sense to me - to the way I think, and
to the kind of code I write. I make decisions about what will work
best. For example, I use "-funsigned-bitfields" because that makes
sense for my code - I have no use for signed bitfields. And I thought
long and hard about "-fwrapv" and whether it would be useful in my code
to be able to rely on modular wrapping of signed integers. My
conclusion was that it would not help - it would not let me write
clearer or more concise code, and it would hinder the optimiser. And of
course I have /lots/ of warnings enabled, so the compiler would tell me
if it used such an optimisation.

Now, I know that I am not an "average" programmer - I have been at this
for decades, I work in a specialised area, and I am well aware that I
have a style and opinions that differ from the "mainstream". But /you/
are not an "average" programmer either. So you can well say that
undefined behaviour for signed overflow is unexpected for /you/, but I
don't think you are correct in thinking that this is a common view. Or
more correctly, I don't think you are correct in thinking that modular
behaviour is the common expectation.

>
> It's as if the compiler replaced "x = foo( i++, i++ )" with "x = 666;",
> with no call of foo(), because that optimization -- yes it's FASTER --
> is allowed by the formal UB, and if the missing call is undesired then
> it's the programmer's own fault surely -- surely the programmer
> intended to have the compiler "optimize" the specified call, yes?

The order of evaluation of "x = foo(i++, i++)" is not undefined
behaviour - it is unspecified behaviour. The compiler can evaluate the
parameters in the order it wants - it can even swap the order between
calls if it wants. But it cannot change the behaviour otherwise.

>
> One simply does not expect a compiler to be that UNREASONABLE. One
> expects it to be reasonable and predictable. With perhaps some option
> for doing very aggressive things when the need for that is great.

In return, the compiler expects the programmer to be reasonable - and
avoid undefined behaviour.

>
> - Alf
>

Öö Tiib

unread,
Oct 9, 2013, 7:22:09 PM10/9/13
to
On Thursday, 10 October 2013 01:15:15 UTC+3, Alf P. Steinbach wrote:
> (2)
> Regarding "that gcc is uniquely and intentionally perverse in not
> following that idea", NO, there is no data that I'm aware of to say
> that the resulting perverseness is intentional, and NO, the
> perverseness does not result from not following the idea of
> changing the language rules.

"Uniquely" I disagree with (jokers are all around) also "perverse" is
maybe too strong but ... historically GCC team has taken steps to
implement the nasal demons (or closest acceptable thing to nasal demons)
before.

Most famous is perhaps GCC 1.17 that upon finding a #pragma directive,
attempted to launch NetHack or Rogue, or start Emacs running a simulation
of the Towers of Hanoi. http://everything2.com/title/%2523pragma

Alf P. Steinbach

unread,
Oct 9, 2013, 8:31:47 PM10/9/13
to
On 10.10.2013 01:11, David Brown wrote:
>
> The order of evaluation of "x = foo(i++, i++)" is not undefined
> behaviour - it is unspecified behaviour. The compiler can evaluate the
> parameters in the order it wants - it can even swap the order between
> calls if it wants. But it cannot change the behaviour otherwise.

You're wrong in two ways:

1. Factually.
It's "undefined", not "unspecified", which is important for
what the compiler is formally allowed to do.

2. Logic.
When you try to show that something is incorrect it does not
suffice to make an assertion. Even if that assertion sounds
pretty plausible. In other words, Herb Schildt-way = ungood.

As of C++11 the relevant language has been moved from section 5 about
expressions to section 1 about general issues, as

C++11 §1.9/15,
"If a side effect on a scalar object is unsequenced relative to either
another side effect on the same scalar object or a value computation
using the value of the same scalar object, the behavior is undefined."


>> One simply does not expect a compiler to be that UNREASONABLE. One
>> expects it to be reasonable and predictable. With perhaps some option
>> for doing very aggressive things when the need for that is great.
>
> In return, the compiler expects the programmer to be reasonable - and
> avoid undefined behaviour.

No, the situation is vastly asymmetric, and you know that.

Try finding out why you get a funny result by setting a breakpoint on
code that has been silently removed.

The compiler, e.g. g++, SHOULD be a tool for the programmer, helping the
programmer, and not require massive efforts to learn about hidden almost
unbelievable subtleties before it can be used.

- Alf

Paavo Helde

unread,
Oct 10, 2013, 2:04:36 AM10/10/13
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in news:l34si4
$lfj$1...@dont-email.me:
> The compiler, e.g. g++, SHOULD be a tool for the programmer, helping the
> programmer, and not require massive efforts to learn about hidden almost
> unbelievable subtleties before it can be used.

I don't get what you have against g++, this is probably the only compiler
which guarantees the wrap-over behavior you are advocating (with the -
fwrapv flag). With other compilers one just has to guess how they behave,
and the results are probably different on different hardware (e.g. DSP).

If anything, it's the C++ standard what should be blamed, for letting such
basic behavior undefined. If some next version of the standard happens to
make the wrap-over mandatory, then g++ folks are well prepared, they have
already implemented the feature and can make it the default in the blink of
an eye.

Cheers
Paavo

Paavo Helde

unread,
Oct 10, 2013, 3:17:19 AM10/10/13
to

P.S. Oblig xkcd: http://xkcd.com/571/

David Brown

unread,
Oct 10, 2013, 3:26:27 AM10/10/13
to
On 10/10/13 02:31, Alf P. Steinbach wrote:
> On 10.10.2013 01:11, David Brown wrote:
>>
>> The order of evaluation of "x = foo(i++, i++)" is not undefined
>> behaviour - it is unspecified behaviour. The compiler can evaluate the
>> parameters in the order it wants - it can even swap the order between
>> calls if it wants. But it cannot change the behaviour otherwise.
>
> You're wrong in two ways:
>
> 1. Factually.
> It's "undefined", not "unspecified", which is important for
> what the compiler is formally allowed to do.
>
> 2. Logic.
> When you try to show that something is incorrect it does not
> suffice to make an assertion. Even if that assertion sounds
> pretty plausible. In other words, Herb Schildt-way = ungood.
>
> As of C++11 the relevant language has been moved from section 5 about
> expressions to section 1 about general issues, as
>
> C++11 §1.9/15,
> "If a side effect on a scalar object is unsequenced relative to either
> another side effect on the same scalar object or a value computation
> using the value of the same scalar object, the behavior is undefined."
>

Looking closer, you are correct (on the "factually" bit). The order of
evaluation of the arguments and their side effects is unspecified (which
was my reasoning), but modifying the value of an object twice between
two sequence points is undefined (which I should have realised).

So your are correct that "x = foo(i++, i++)" is undefined behaviour, and
the compiler can legally "optimise" it to "x = 666". I think you are
also correct in suggesting that this would be a /bad/ thing for the
compiler to do.

You will be pleased to know that gcc (testing with a sample of one small
program) handles this sensibly by effectively doing two "i++" statements
in an unspecified order. And if you have warnings enabled (as you
always should), it will tell you the operation may be undefined.


(I disagree on your complaint about my logic. This is Usenet, not a
court of law. It /does/ suffice to make an assertion in many cases -
this is a self-correcting medium, and details and corrections appear
magically when needed. :-)

>
>>> One simply does not expect a compiler to be that UNREASONABLE. One
>>> expects it to be reasonable and predictable. With perhaps some option
>>> for doing very aggressive things when the need for that is great.
>>
>> In return, the compiler expects the programmer to be reasonable - and
>> avoid undefined behaviour.
>
> No, the situation is vastly asymmetric, and you know that.

Certainly I expect the compiler writers to have a tighter grasp of the
standards than I do, as a mere user. (As mentioned before, I have a
good understanding of the standards as they apply to the type of code I
work with - but compiler writers have to cope with code like "foo(i++,
i++)" that would never appear in a real program and is normally only
ever seen in bad interview questions.)

But C, and C++ even more so, has never been a beginner-friendly
language. You /are/ expected to know roughly what you are doing. The
compiler can try to be helpful, if you let it (by enabling warnings).
But it can't protect you from yourself. There are languages will far
less undefined behaviour that can be legally written, and will often be
a better choice.

Of course I agree with you that the compiler should avoid being
unexpected or unreasonable. And if it can make a reasonable guess about
the intentions of undefined behaviour (such as in your foo(i++, i++)
example), then it should do so, rather than making demons fly out of
your nose. But when there can be no reasonable guess of the
programmer's intentions, such as for signed overflow, the compiler can
do as it wants.

>
> Try finding out why you get a funny result by setting a breakpoint on
> code that has been silently removed.

That is part of the challenge of debugging optimised code, and is
completely independent of the topic at hand. When optimising, code is
regularly removed, changed, moved around, merged, split up, and so on.
It makes the job far more entertaining.

>
> The compiler, e.g. g++, SHOULD be a tool for the programmer, helping the
> programmer, and not require massive efforts to learn about hidden almost
> unbelievable subtleties before it can be used.
>

I have used gcc (with C mainly, only a little with C++) for over 15
years on a dozen different architectures and I cannot say I have come
across anything I would call "hidden almost unbelievable subtleties".

There are subtleties in the C language, and some of these have become
more apparent as compilers have improved. A couple that spring to mind,
as they turn up in embedded programming, are alias analysis (it is not
uncommon to manipulate data using pointers of different types, or do
other low-level conversions) and the ordering of volatile and
non-volatile accesses (many people mistakenly think volatile accesses
impose and order on all accesses).

I don't see any of these as being gcc-specific - though simpler
compilers will act in a more obvious way, producing big, slow code with
a one-to-one correspondence with the source. Getting the best out of
gcc, or any compiler, means knowing your compiler (and your language)
well, and working with it in cooperation.

David Brown

unread,
Oct 10, 2013, 3:34:18 AM10/10/13
to
If that joke from 20 years ago is the worst nasal demon from the gcc
developers, then I am not worried.

Öö Tiib

unread,
Oct 10, 2013, 4:59:50 AM10/10/13
to
On Thursday, 10 October 2013 10:34:18 UTC+3, David Brown wrote:
> On 10/10/13 01:22, Öö Tiib wrote:
> > Most famous is perhaps GCC 1.17 that upon finding a #pragma directive,
> > attempted to launch NetHack or Rogue, or start Emacs running a simulation
> > of the Towers of Hanoi. http://everything2.com/title/%2523pragma
>
> If that joke from 20 years ago is the worst nasal demon from the gcc
> developers, then I am not worried.

It is the most famous one. g++ is otherwise very useful tool and I can
easily tolerate few jokes. Apple's C++ compiler was also full of subtle
jokes. I am content with it, even been happy with it and not worried
at all. What is a day worth with zero jokes?

Correct hacker's attitude is that if you do not like something (a joke
got old) then you beat it into shape. g++ however is masterpiece there
too, it is rather fun to try and hack it.

Correct engineer's attitude is that if you do not like something then
you make better one. That has been done: Weakness of g++ diagnostics,
tendency of it to "optimize" on UB (instead of diagnosing) and
unsuitability for integration with other tools were the prime drivers
of Clang coming to be. They even say it directly on front page if
you read it carefully. http://clang.llvm.org/

So ... it is rather deep discussion you guys have picked up. ;)

Alf P. Steinbach

unread,
Oct 10, 2013, 5:01:24 AM10/10/13
to
On 10.10.2013 08:04, Paavo Helde wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in news:l34si4
> $lfj$1...@dont-email.me:
>> The compiler, e.g. g++, SHOULD be a tool for the programmer, helping the
>> programmer, and not require massive efforts to learn about hidden almost
>> unbelievable subtleties before it can be used.
>
> I don't get what you have against g++,

Not surprising, since that has not been discussed.

A long thread about someone's denial of a single wart can make that wart
seem really really important, but in the big scheme it's still just a
wart. I think the denial is important. The wart itself, no.

It was absolutely not unexpected, because it regularly happens with
discussions of warts involving Microsoft, g++ compiler and/or Python
language, the 3 big fanboï groups.

I just fear for the thread, in some forum, involving all 3.

And so I write in my FIRST answer to David Brown, in the signature, that
I was "noting that the holiness of g++ is a religious issue".

I.e. I knew very well what would happen, I knew the (probable) resulting
length of the thread and its noise factor, and I decided, I hope
correctly, that it was best to have this over and done with.

Now, since you ask, what I have against g++ is mainly

* Very incomplete support for Windows:

- Lack of library support for the Windows API after Windows XP,
i.e. for Windows Vista, Windows 7 and Windows 8.

- Incomplete tool support for standard Windows resource data.

- Lack of tool support for Windows "COM" servers, and to some
degree Windows DLLs in general.

* Unpredictability in general:

- Too "clever" by far aggressive optimizations (e.g. current wart
discussion).

* Lagging support for the C++ standard library:

- In the old days, lacking support for wide streams and strings.

- Currently, lacking support for threading library (I think that
was just implemented, but not yet available for Windows),
regexp, etc.

That said -- because you asked :-) -- g++ is generally very much
more standard-conforming and up to date regarding the core language
parts of the standard, than Visual C++.

So, it's a nice and often necessary #2 compiler even in Windows.


> this is probably the only compiler
> which guarantees the wrap-over behavior you are advocating (with the -
> fwrapv flag). With other compilers one just has to guess how they behave,
> and the results are probably different on different hardware (e.g. DSP).

I disagree, because this is all about the in-practice, and for the
in-practice I have not heard of ANY example of
non-modular-signed-arithmetic C++ compiler the last 10 years.

So I think we're well past that point. :-)


> If anything, it's the C++ standard what should be blamed, for letting such
> basic behavior undefined. If some next version of the standard happens to
> make the wrap-over mandatory, then g++ folks are well prepared, they have
> already implemented the feature and can make it the default in the blink of
> an eye.

Yeps. :-)


Cheers,

- Alf

David Brown

unread,
Oct 10, 2013, 7:32:53 AM10/10/13
to
On 10/10/13 11:01, Alf P. Steinbach wrote:
> On 10.10.2013 08:04, Paavo Helde wrote:
>> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
>> news:l34si4
>> $lfj$1...@dont-email.me:
>>> The compiler, e.g. g++, SHOULD be a tool for the programmer, helping the
>>> programmer, and not require massive efforts to learn about hidden almost
>>> unbelievable subtleties before it can be used.
>>
>> I don't get what you have against g++,
>
> Not surprising, since that has not been discussed.
>
> A long thread about someone's denial of a single wart can make that wart
> seem really really important, but in the big scheme it's still just a
> wart. I think the denial is important. The wart itself, no.

I guess that means /you/ think that gcc's handling of signed overflow
optimisations is a "wart", albeit it not a very important wart, while I
deny that it is a wart at all.

I don't think we will ever agree on that issue, so may we should stop
and just accept the difference of opinions.

>
> It was absolutely not unexpected, because it regularly happens with
> discussions of warts involving Microsoft, g++ compiler and/or Python
> language, the 3 big fanboï groups.
>
> I just fear for the thread, in some forum, involving all 3.
>
> And so I write in my FIRST answer to David Brown, in the signature, that
> I was "noting that the holiness of g++ is a religious issue".

If we are going to get holy, you should get the name write - the tool is
called "gcc". g++ is a front-end that calls gcc with a couple of
libraries added for the link.

While I know that some people do see their favourite tools as "holy", I
am not one of them. I like gcc - I use it a lot, and have done for many
years. Like all complex software, it has its faults and limitations -
but I find it very useful despite its warts.

>
> I.e. I knew very well what would happen, I knew the (probable) resulting
> length of the thread and its noise factor, and I decided, I hope
> correctly, that it was best to have this over and done with.
>
> Now, since you ask, what I have against g++ is mainly
>
> * Very incomplete support for Windows:
>
> - Lack of library support for the Windows API after Windows XP,
> i.e. for Windows Vista, Windows 7 and Windows 8.
>
> - Incomplete tool support for standard Windows resource data.
>
> - Lack of tool support for Windows "COM" servers, and to some
> degree Windows DLLs in general.
>

gcc is a toolchain, not a library (other than the standard C and C++
libraries), and it is not a OS-specific development tool. It does not
have libraries or headers for Windows - nor does it have libraries or
headers for Linux. The difference is that Linux installations typically
have headers and libraries on-hand, so they are easy to use - while in
Windows you have nothing of the sort.

If you want to program in C or C++ for Windows, you should either use a
Windows-specific tool (such as MSVC or Borland C++), or use a toolkit
such as wxWidgets or QT along with gcc.

So this is not a failing of gcc - it is a misunderstanding of what gcc
actually is.

(I've only done small command-line programs with gcc in Windows, and
it's fine for that.)


> * Unpredictability in general:
>
> - Too "clever" by far aggressive optimizations (e.g. current wart
> discussion).
>

Given that I don't accept the signed overflow behaviour as a "wart", and
I haven't heard anyone else say so, you'll have to come up with a better
example. Generalising from a single poor and controversial example does
not strengthen your case.

I have heard others complain about the accuracy of its floating point
code and question the strictness of its adherence to IEEE standards. I
don't know enough about this to comment or to compare it to other
toolchains (it's certainly good enough for what I do - and I don't want
the code to waste time sticking to IEEE if it can get real-world useful
results faster by taking shortcuts).


> * Lagging support for the C++ standard library:
>
> - In the old days, lacking support for wide streams and strings.

I have never used such things myself, but you imply that this is no
longer an issue.

>
> - Currently, lacking support for threading library (I think that
> was just implemented, but not yet available for Windows),
> regexp, etc.


Here are the relevant status pages:

<http://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.200x>
<http://gcc.gnu.org/projects/cxx0x.html>
<http://gcc.gnu.org/wiki/C11Status>
<http://gcc.gnu.org/c99status.html>

Whether the missing points are relevant to you will depend on your needs.

mvh.,

David

David Brown

unread,
Oct 10, 2013, 7:37:39 AM10/10/13
to
On 10/10/13 10:59, 嘱 Tiib wrote:
> On Thursday, 10 October 2013 10:34:18 UTC+3, David Brown wrote:
>> On 10/10/13 01:22, 嘱 Tiib wrote:
>>> Most famous is perhaps GCC 1.17 that upon finding a #pragma directive,
>>> attempted to launch NetHack or Rogue, or start Emacs running a simulation
>>> of the Towers of Hanoi. http://everything2.com/title/%2523pragma
>>
>> If that joke from 20 years ago is the worst nasal demon from the gcc
>> developers, then I am not worried.
>
> It is the most famous one. g++ is otherwise very useful tool and I can
> easily tolerate few jokes. Apple's C++ compiler was also full of subtle
> jokes. I am content with it, even been happy with it and not worried
> at all. What is a day worth with zero jokes?
>
> Correct hacker's attitude is that if you do not like something (a joke
> got old) then you beat it into shape. g++ however is masterpiece there
> too, it is rather fun to try and hack it.

When you say "fun", I assume you mean the fun of the challenge? Some
parts of gcc's code base are okay, but there is a lot that is barely
comprehensible to the experts.

>
> Correct engineer's attitude is that if you do not like something then
> you make better one. That has been done: Weakness of g++ diagnostics,
> tendency of it to "optimize" on UB (instead of diagnosing) and
> unsuitability for integration with other tools were the prime drivers
> of Clang coming to be. They even say it directly on front page if
> you read it carefully. http://clang.llvm.org/
>

I think the competition has been good for both gcc and llvm. One
obvious case is that Clang had far more helpful error messages - now the
gcc error and warning messages have had greater improvement in the past
couple of versions than for many years previously.

Alf P. Steinbach

unread,
Oct 10, 2013, 12:28:23 PM10/10/13
to
On 10.10.2013 13:32, David Brown wrote:
>
> gcc is a toolchain, not a library (other than the standard C and C++
> libraries), and it is not a OS-specific development tool. It does not
> have libraries or headers for Windows - nor does it have libraries or
> headers for Linux. The difference is that Linux installations typically
> have headers and libraries on-hand, so they are easy to use - while in
> Windows you have nothing of the sort.

You contention that those libraries, like in my g++ installation
"libshlwapi.a" etc., don't exist, is a denial of reality.

It's insane.

Without the proper headers and import libraries the API level Windows
programs and libraries that others and I make with g++, would never
build, novices with old machines could not install Code::Blocks or
whatever g++-based IDE and go for it, so on.

You can make your statement "true" in a literal sense, like Bill
Clinton's "I did not have 'sex' with that woman!", by suitably narrowing
your definition of "gcc".

For it is most probably not the GNU gang but rather Cygwin and MinGW
folks who maintain the Windows-specific libs, and then one can choose a
suitably narrow definition of "gcc" that excludes Windows versions.

With such interpretation your statement is idiotic in context, sorry!,
just like "I did not have 'sex'".


- Alf

Tobias Müller

unread,
Oct 10, 2013, 12:39:24 PM10/10/13
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
[...]
> It's as if the compiler replaced "x = foo( i++, i++ )" with "x = 666;",
> with no call of foo(), because that optimization -- yes it's FASTER -- is
> allowed by the formal UB, and if the missing call is undesired then it's
> the programmer's own fault surely -- surely the programmer intended to
> have the compiler "optimize" the specified call, yes?

This comparison just does not hold. The above is UB in any case and it is
clear even at compile time. There is no possibility of defined behavior and
any attempt of "optimization" is worthless anyway because the programmers
intention is not clear at all. A compiler warning (or even error, if
allowed) would be sensible here.

Signed integer addition OTOH is well defined for most cases. UB is only
clear at runtime. In this case, optimizing the the well defined cases makes
sense IMO.

[...]

Tobi

Alf P. Steinbach

unread,
Oct 10, 2013, 1:33:32 PM10/10/13
to
No, the signed arithmetic optimizations that are troublesome are where
the result is known at compile time, even if not obvious or visible to
the programmer.

I do not know of any optimization of the general run time behavior of
signed integer addition that come in addition to general optimizations
like replacing "i = i + 1" with an increment instruction.

Leigh Johnston

unread,
Oct 10, 2013, 2:18:40 PM10/10/13
to
I really don't get why you guys are still arguing; it is really quite
simple:

Signed integer overflow is undefined behaviour so DON'T DO IT.

Even simpler:

IF IT IS UNDEFINED BEHAVIOUR THEN DON'T DO IT.

End of discussion.

/Leigh

Öö Tiib

unread,
Oct 10, 2013, 2:38:18 PM10/10/13
to
On Thursday, 10 October 2013 14:37:39 UTC+3, David Brown wrote:
> On 10/10/13 10:59, Öö Tiib wrote:
> > Correct hacker's attitude is that if you do not like something (a joke
> > got old) then you beat it into shape. g++ however is masterpiece there
> > too, it is rather fun to try and hack it.
>
> When you say "fun", I assume you mean the fun of the challenge? Some
> parts of gcc's code base are okay, but there is a lot that is barely
> comprehensible to the experts.

Exactly, that I meant with fun. Experts like challenge to not get rusty.
Especially real challenges. Unfortunately lot of people are not experts
and even experts integrating their tool-set are often impatient when the
challenge is boring and they are not in mood for games today.

> > Correct engineer's attitude is that if you do not like something then
> > you make better one. That has been done: Weakness of g++ diagnostics,
> > tendency of it to "optimize" on UB (instead of diagnosing) and
> > unsuitability for integration with other tools were the prime drivers
> > of Clang coming to be. They even say it directly on front page if
> > you read it carefully. http://clang.llvm.org/
>
> I think the competition has been good for both gcc and llvm. One
> obvious case is that Clang had far more helpful error messages - now the
> gcc error and warning messages have had greater improvement in the past
> couple of versions than for many years previously.

The competition has pushed gcc towards average taste and now it treats
developers more like users and less like targets of jokes. It is because
the average people writing C++ do not like to deal with the jokes.
They want to deal with the data structures, algorithms and communications
of their program.

Tobias Müller

unread,
Oct 10, 2013, 4:34:32 PM10/10/13
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
> On 10.10.2013 18:39, Tobias Müller wrote:
>> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
>> [...]
>>> It's as if the compiler replaced "x = foo( i++, i++ )" with "x = 666;",
>>> with no call of foo(), because that optimization -- yes it's FASTER -- is
>>> allowed by the formal UB, and if the missing call is undesired then it's
>>> the programmer's own fault surely -- surely the programmer intended to
>>> have the compiler "optimize" the specified call, yes?
>>
>> This comparison just does not hold. The above is UB in any case and it is
>> clear even at compile time. There is no possibility of defined behavior and
>> any attempt of "optimization" is worthless anyway because the programmers
>> intention is not clear at all. A compiler warning (or even error, if
>> allowed) would be sensible here.
>>
>> Signed integer addition OTOH is well defined for most cases. UB is only
>> clear at runtime. In this case, optimizing the the well defined cases makes
>> sense IMO.
>
> No, the signed arithmetic optimizations that are troublesome are where
> the result is known at compile time, even if not obvious or visible to the programmer.

I didn't say that the _optimizations_ are only visible at runtime,
optimizations are obviously always made at compile time. But this is not
restricted to constant propagation, but also symbolic optimizations that
still allow for different values at runtime.

> I do not know of any optimization of the general run time behavior of
> signed integer addition that come in addition to general optimizations
> like replacing "i = i + 1" with an increment instruction.

x < x + 1 ==> true
x * 2 / 2 ==> x
(found here:
http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html?m=1)

I don't fully understand the for-loop example on that page, but it seems to
enable various loop optimizations.

Tobi

Alf P. Steinbach

unread,
Oct 10, 2013, 4:58:57 PM10/10/13
to
Yeah, this is what we've been talking about (so, obviously known), and
no, these are not optimizations of the run time behavior.

Anyway, note well that if a C++ programmer writes

if( x < x + 1 )

then the programmer does NOT mean "if( true )".

Rather, the programmer is then exploiting platform-specific
functionality, most probably compiling with g++ code originally written
for some other compiler.

So it's just wrong to optimize that, regardless of assumptions about the
arithmetic. It certainly does not help any to optimize it. It's like,
wrong no matter how you regard it, like, always wrong, you know.

More importantly, such aggressive and non-intuitive micro-optimizations
that affect intended meaning and therefore correctness, can be TURNED ON
instead of having to be turned off.


> I don't fully understand the for-loop example on that page, but it seems to
> enable various loop optimizations.

It's good that you don't understand it, because the description is
fallacious and wrong. In particular, for the example

for (i = 0; i <= N; ++i) { ... }

the description "if the variable [namely 'i'] is defined to wrap around
on overflow, then the compiler must assume that the loop is possibly
infinite" is false. The author may be thinking of the case where N is
the maximum signed value, but that's easy to check for, and even easier
for the programmer to specify if e.g. loop unrolling is desired.

I can only think of one reason for writing such a blatantly false and
misleading statement, and that is a blind faith in and acceptance
gcc/clang so that the author's think-box is completely turned off.

Paavo Helde

unread,
Oct 10, 2013, 5:23:55 PM10/10/13
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
news:l374fb$uh0$1...@dont-email.me:

> Anyway, note well that if a C++ programmer writes
>
> if( x < x + 1 )

In my practice, this kind of expressions arise from templated code,
possibly after inlining some functors passed as template parameters, so
in the actual source code the expression would not look anything like
above, and the type of the x could be for example whatever numeric type,
either signed or unsigned.

I would appreciate greatly if the compiler would be able to optimize such
code as much as possible, for example eliding any x<0 code branches for
unsigned types, etc.

> then the programmer does NOT mean "if( true )".

If the resulting expression after all template inlining is 'x<x+1' for a
signed integer type then surely I am meaning 'true'.

> Rather, the programmer is then exploiting platform-specific
> functionality, most probably compiling with g++ code originally
> written for some other compiler.

Then he should not be surprised that his implementation-specific code
ceases to work when ported to another implementation. Why can't he write
portable code like 'if (x<INT_MAX) which would express the intent much
better?

Cheers
Paavo



Tobias Müller

unread,
Oct 10, 2013, 6:10:10 PM10/10/13
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
> On 10.10.2013 22:34, Tobias Müller wrote:
>> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
>>
>>> I do not know of any optimization of the general run time behavior of
>>> signed integer addition that come in addition to general optimizations
>>> like replacing "i = i + 1" with an increment instruction.
>>
>> x < x + 1 ==> true
>> x * 2 / 2 ==> x
>> (found here:
>> http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html?m=1)
>
> Yeah, this is what we've been talking about (so, obviously known), and
> no, these are not optimizations of the run time behavior.
>
> Anyway, note well that if a C++ programmer writes
>
> if( x < x + 1 )
>
> then the programmer does NOT mean "if( true )".
>
> Rather, the programmer is then exploiting platform-specific
> functionality, most probably compiling with g++ code originally written
> for some other compiler.

No, writing this code literally and thus _actively_ relying on UB is just
stupid, I thought we agree on that.
I don't want to miss important optimization opportunities because of some
people writing stupid code.

> So it's just wrong to optimize that, regardless of assumptions about the
> arithmetic. It certainly does not help any to optimize it. It's like,
> wrong no matter how you regard it, like, always wrong, you know.

No, it is essential, especially in combination with templates.
In the generic form, the optimizations are usually not possible, but in a
specific instanciation they may be important.
Similar for inlining and whole program optimization.

> More importantly, such aggressive and non-intuitive micro-optimizations
> that affect intended meaning and therefore correctness, can be TURNED ON
> instead of having to be turned off.

They only affect the meaning of badly written code!

>> I don't fully understand the for-loop example on that page, but it seems to
>> enable various loop optimizations.
>
> It's good that you don't understand it, because the description is fallacious and wrong.

Despite your experience, in this case I trust the llvm devs. Please don't
take this as a personal attack, it's not.

> In particular, for the example
>
> for (i = 0; i <= N; ++i) { ... }
>
> the description "if the variable [namely 'i'] is defined to wrap around
> on overflow, then the compiler must assume that the loop is possibly
> infinite" is false. The author may be thinking of the case where N is the
> maximum signed value, but that's easy to check for, and even easier for
> the programmer to specify if e.g. loop unrolling is desired.

I hope you don't expect me to annotate my code with all desired
optimizations.
The test if N == INT_MAX would still have to be done at runtime and handled
properly. This means you have one optimized and one unoptimized
(superfluous) version of the loop in your binary.

[snip attacks on clang/gcc devs]

Tobi

Alf P. Steinbach

unread,
Oct 10, 2013, 6:19:03 PM10/10/13
to
On 10.10.2013 23:23, Paavo Helde wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
> news:l374fb$uh0$1...@dont-email.me:
>
>> Anyway, note well that if a C++ programmer writes
>>
>> if( x < x + 1 )
>
> In my practice, this kind of expressions arise from templated code,
> possibly after inlining some functors passed as template parameters, so
> in the actual source code the expression would not look anything like
> above, and the type of the x could be for example whatever numeric type,
> either signed or unsigned.
>
> I would appreciate greatly if the compiler would be able to optimize such
> code as much as possible, for example eliding any x<0 code branches for
> unsigned types, etc.
>
>> then the programmer does NOT mean "if( true )".
>
> If the resulting expression after all template inlining is 'x<x+1' for a
> signed integer type then surely I am meaning 'true'.

Well, in this case the programmer didn't write it.

And if 'x<x+1' is produced by template inlining then 'x' is presumably
known at compile time, or do I misunderstand what you have in mind?

And then the expression can be reduced directly without recourse to
general properties of the arithmetic operations, i.e. without exploiting
formal UB by assuming infinite precision arithmetic.


>> Rather, the programmer is then exploiting platform-specific
>> functionality, most probably compiling with g++ code originally
>> written for some other compiler.
>
> Then he should not be surprised that his implementation-specific code
> ceases to work when ported to another implementation.

For established platform conventions, he (or she) should, IMHO, be
surprised that the compiler doesn't honor them by default.

Even weak conventions like this.


> Why can't he write
> portable code like 'if (x<INT_MAX) which would express the intent much
> better?

I totally agree. :-)

But then, there are any number of reasons: some programmers insist on
being "clever" (or, all do, at some times?), some constructs result from
editing and paring down, some code is just copied around, ...


Cheers,

- Alf

K. Frank

unread,
Oct 10, 2013, 6:26:32 PM10/10/13
to
Hello Alf!

On Thursday, October 10, 2013 5:01:24 AM UTC-4, Alf P. Steinbach wrote:
> ...
> Now, since you ask, what I have against g++ is mainly
> ...
>
> * Lagging support for the C++ standard library:

I find that current versions of g++ have very good
support for the c++11 standard library (and c++11
language features). Not totally complete, but almost
all of the features I've found myself wanting use
have been there.

> - In the old days, lacking support for wide streams and strings.
>
> - Currently, lacking support for threading library (I think that
> was just implemented, but not yet available for Windows),
> regexp, etc.

I have been using std::thread with g++ for four years
now, on windows, no less. Basic support for std::thread
(and related features) has been around for a while now
(years) and has become more complete over time. I won't
go so far as to state that it is fully complete or error
free, but g++'s support for std::thread is currently at
least nearly complete, and I have used it without incident
in real-world code.

> ...
> Cheers,
>
> - Alf


Best.


K. Frank

Alf P. Steinbach

unread,
Oct 10, 2013, 7:08:29 PM10/10/13
to
On 11.10.2013 00:10, Tobias Müller wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
>> On 10.10.2013 22:34, Tobias Müller wrote:
>>> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
>>>
>>>> I do not know of any optimization of the general run time behavior of
>>>> signed integer addition that come in addition to general optimizations
>>>> like replacing "i = i + 1" with an increment instruction.
>>>
>>> x < x + 1 ==> true
>>> x * 2 / 2 ==> x
>>> (found here:
>>> http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html?m=1)
>>
>> Yeah, this is what we've been talking about (so, obviously known), and
>> no, these are not optimizations of the run time behavior.
>>
>> Anyway, note well that if a C++ programmer writes
>>
>> if( x < x + 1 )
>>
>> then the programmer does NOT mean "if( true )".
>>
>> Rather, the programmer is then exploiting platform-specific
>> functionality, most probably compiling with g++ code originally written
>> for some other compiler.
>
> No, writing this code literally and thus _actively_ relying on UB is just
> stupid, I thought we agree on that.

Oh yes.

Don't do that.


> I don't want to miss important optimization opportunities because of some
> people writing stupid code.

You don't have to miss out on speed. For example, for the Windows
platform Visual C++ (to take one example) generally produces faster code
than gcc, and Intel (which I'm not experienced with) faster still, it's
said. Without those silly breaking optimizations.


>> So it's just wrong to optimize that, regardless of assumptions about the
>> arithmetic. It certainly does not help any to optimize it. It's like,
>> wrong no matter how you regard it, like, always wrong, you know.
>
> No, it is essential, especially in combination with templates.
> In the generic form, the optimizations are usually not possible, but in a
> specific instanciation they may be important.
> Similar for inlining and whole program optimization.
>
>> More importantly, such aggressive and non-intuitive micro-optimizations
>> that affect intended meaning and therefore correctness, can be TURNED ON
>> instead of having to be turned off.
>
> They only affect the meaning of badly written code!

So? How do you propose to avoid all such code?


>>> I don't fully understand the for-loop example on that page, but it seems to
>>> enable various loop optimizations.
>>
>> It's good that you don't understand it, because the description is fallacious and wrong.
>
> Despite your experience, in this case I trust the llvm devs. Please don't
> take this as a personal attack, it's not.

It's OK, my explanation was just not clear enough. So, thanks for that
feedback. ;-)


>> In particular, for the example
>>
>> for (i = 0; i <= N; ++i) { ... }
>>
>> the description "if the variable [namely 'i'] is defined to wrap around
>> on overflow, then the compiler must assume that the loop is possibly
>> infinite" is false. The author may be thinking of the case where N is the
>> maximum signed value, but that's easy to check for, and even easier for
>> the programmer to specify if e.g. loop unrolling is desired.

I omitted this sentence continuation for brevity in the quote above: "-
which then disables these important loop optimization".

Even if you disagree with the notion that "must assume" is false,
clearly the bit about disabling the optimization is false.

After all, they can easily be done, which contradicts that they cannot
be done.

Is that clear enough explanation?

Oh well, to prove that for yourself if you don't see it, simply look up
Duff's Device in Wikipedia (if you're not familiar with it), and
hand-craft that optimization for a relevant loop with signed loop
variable where N can possibly but necessarily be INT_MAX.

So, even more importantly than the incorrect "must assume", the claim
that this loop would be impossible to optimize is completely and utterly
false.

And so, with errors and misdirections galore, all of them pointing in
one direction, for the purely technical, for what you assume internally
for your programming, it's NOT a good idea to trust that document.

Rather trust your instinct, that when you initially find that, as you
wrote, you "don't fully understand" it, and it's about something simple,
then there's most likely something fishy, something not right, and when
written by intelligent people it's likely misdirection. :)


> I hope you don't expect me to annotate my code with all desired
> optimizations.

No, that would be a rather impractical approach.

Can you think of anything more practical?


> The test if N == INT_MAX would still have to be done at runtime and handled
> properly.

No, but knowledge about N has to be established either at compile time
or at run time.

Note that this is the same as for unsigned loop variable.

Effectively, therefore, the author(s) pretend that people are using
signed loop variables in order to allow gcc to silently exploit formal
UB to optimize away two machine code instructions for the case where N
is not known to be in reasonable range, and that it's important to have
this micro-optimization invoked in that very subtle and implicit way.

Which is just silly.

At least when you reflect on it a bit.


> This means you have one optimized and one unoptimized
> (superfluous) version of the loop in your binary.

No, but that's the worst case.

One far more PRACTICAL approach is that the compiler warns when the loop
can be infinite and unrolling has been asked for; then it can be
rewritten or an option supplied that says "assume finite loops".

Same with unsigned loop variable. ;-)

Alf P. Steinbach

unread,
Oct 10, 2013, 7:32:51 PM10/10/13
to
On 11.10.2013 00:26, K. Frank wrote:
>
> I have been using std::thread with g++ for four years
> now, on windows, no less. Basic support for std::thread
> (and related features) has been around for a while now
> (years) and has become more complete over time. I won't
> go so far as to state that it is fully complete or error
> free, but g++'s support for std::thread is currently at
> least nearly complete, and I have used it without incident
> in real-world code.

Thanks!

I now googled up this SO question about it: <url:
http://stackoverflow.com/questions/16227620/mingw-with-g-4-7-1-not-compiling-simple-c11-code-under-winvista>.

So, I've evidently been exposed to the wrong MinGW builds re <thread>,

g++ library status (this looks current):

http://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.2011

No regexp yet -- but then, as the adage goes, if one has a problem,
and thinks the solution is regular expressions, then one has 2 problems.


Cheers,

- Alf
By mistake this was mailed first, instead of posted. Thunderbird. Sorry.

Paavo Helde

unread,
Oct 11, 2013, 1:01:23 AM10/11/13
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
news:l3795i$ssq$1...@dont-email.me:
> On 10.10.2013 23:23, Paavo Helde wrote:

>> If the resulting expression after all template inlining is 'x<x+1'
>> for a signed integer type then surely I am meaning 'true'.
>
> Well, in this case the programmer didn't write it.

Irrelevant, this is what the optimizer sees at one step. Or are you
suggesting the optimizer should start to guess what the programmer had in
mind, and produce different code based on that?

> And if 'x<x+1' is produced by template inlining then 'x' is presumably
> known at compile time, or do I misunderstand what you have in mind?

No, this is not what I had in my mind. The type of x would be (one of)
template parameters, but the values are known only at run-time. I am mostly
concerned about array operations, where the type of array elements is not
fixed and the code must support all (numeric) types, for example.

Cheers
Paavo


Alf P. Steinbach

unread,
Oct 11, 2013, 4:07:35 AM10/11/13
to
On 11.10.2013 07:01, Paavo Helde wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
> news:l3795i$ssq$1...@dont-email.me:
>> On 10.10.2013 23:23, Paavo Helde wrote:
>
>>> If the resulting expression after all template inlining is 'x<x+1'
>>> for a signed integer type then surely I am meaning 'true'.
>>
>> Well, in this case the programmer didn't write it.
>
> Irrelevant, this is what the optimizer sees at one step. Or are you
> suggesting the optimizer should start to guess what the programmer had in
> mind, and produce different code based on that?

Far from the compiler adding EXTRA unreliability, as you suggest, it
should instead be (more) predictable, more reliable.

The programmer should not have to guess at the compiler.

No guessing or telepathy is needed for that, but instead, common sense.


>> And if 'x<x+1' is produced by template inlining then 'x' is presumably
>> known at compile time, or do I misunderstand what you have in mind?
>
> No, this is not what I had in my mind. The type of x would be (one of)
> template parameters, but the values are known only at run-time. I am mostly
> concerned about array operations, where the type of array elements is not
> fixed and the code must support all (numeric) types, for example.

Perhaps you could give a CONCRETE EXAMPLE?

Because I'm baffled as to how "x < x+1" could be produced, with x a
variable and the original code meaningful.

Cheers,

- Alf

Robert Wessel

unread,
Oct 11, 2013, 4:34:57 AM10/11/13
to
One possibility is as the result of being able to determine the values
of other variable in the original equation. Let's say you started
with:

if ((x*a) < (x+b))

And we happen to know, perhaps because of the parameters passed to a
template instantiation or an inlined function, that a and b are both
one.

That sort of optimization is fundamental to why inlined functions can
produce significant performance gains. And the next thing I'd expect
would be for the compiler to remove the dead code in the "else" leg,
and then possibly find dead variable to eliminate, and then be better
able to optimize the containing loop (perhaps strength reduction
becomes possible with the loss of the conditional), etc.

Or we can throw that all away and assume the programmer wants to test
undefined behavior. To be sure there's a balance to be struck here
someplace.

David Brown

unread,
Oct 11, 2013, 5:00:30 AM10/11/13
to
Bingo - you've found the answer!

The headers and libraries needed for Windows-specific development are
not part of gcc, but are often included with downloads or installation
packages. If you didn't get everything you wanted along with mingw, or
cygwin, or Code::Blocks, then complain to /them/.

What are you going to moan about next? That gcc doesn't come with a
good editor?

David Brown

unread,
Oct 11, 2013, 5:09:50 AM10/11/13
to
It is quite simple - "N" is not a compile-time constant. The compiler
may be able to generate better code knowing that the loop will always
end and that "i" will always be non-negative (assuming undefined
behaviour for integer overflow), than it could do without that assumption.


David Brown

unread,
Oct 11, 2013, 5:19:09 AM10/11/13
to
On 11/10/13 00:19, Alf P. Steinbach wrote:

> For established platform conventions, he (or she) should, IMHO, be
> surprised that the compiler doesn't honor them by default.
>

This is the key point where you are completely and utterly wrong, and
which forms the basis for all your misunderstandings and extrapolations.

The established convention is that signed overflow is undefined
behaviour. This matches the C standards, it matches what works well for
the hardware of typical systems, it matches what works well for compiler
optimisations, and it matches what human programmers expect.

For some unknown reason, you seem to think that "ordinary" programmers
expect that signed integers work as modulo arithmetic. In reality, even
those that don't really understand the idea of "undefined behaviour"
grasp the concept that if you overflow, things go wrong. Ask a
five-year-old to do 9 plus 2 on his fingers - he will tell you it can't
be done without more fingers.

Alf P. Steinbach

unread,
Oct 11, 2013, 10:03:54 AM10/11/13
to
On 11.10.2013 10:34, Robert Wessel wrote:
> On Fri, 11 Oct 2013 10:07:35 +0200, "Alf P. Steinbach"
>>
>> Perhaps you could give a CONCRETE EXAMPLE?
>>
>> Because I'm baffled as to how "x < x+1" could be produced, with x a
>> variable and the original code meaningful.
>
> One possibility is as the result of being able to determine the values
> of other variable in the original equation. Let's say you started
> with:
>
> if ((x*a) < (x+b))
>
> And we happen to know, perhaps because of the parameters passed to a
> template instantiation or an inlined function, that a and b are both
> one.

You have convinced me, that expression, "x < x+1", CAN happen for good
reasons.

Thanks.


> That sort of optimization is fundamental to why inlined functions can
> produce significant performance gains. And the next thing I'd expect
> would be for the compiler to remove the dead code in the "else" leg,
> and then possibly find dead variable to eliminate, and then be better
> able to optimize the containing loop (perhaps strength reduction
> becomes possible with the loss of the conditional), etc.
>
> Or we can throw that all away and assume the programmer wants to test
> undefined behavior. To be sure there's a balance to be struck here
> someplace.

Well that sounds rather ominous, as if guaranteed wrap-around behavior
for this would impose some unacceptable inefficiency in some cases.

A standards change in that direction, which is the outer discussion
topic one level up the call stack (so to speak), would maybe force
compilers to introduce such inefficiency, like comparing two integers,
instead of that being the existing practice as I have maintained?

Let's check REALITY then.

Like, what does real Windows compilers do with that construct, when
asked to optimize?

Do they optimize the resulting x <= x+1 to "true", or do they introduce
the unacceptable inefficiency of performing the comparison or even
optimizing x <= x+1 to its wrap-around behavior "false" for known x?


[code]
#include <iostream>
using namespace std;

void fire_nukes_at( int const destination )
{
auto const victim = (destination >= 0? "them" : "ourselves");
cout << " Firing nukes at " << victim << "!" << endl;
}

template< int a, int b >
void foo( int const x )
{
if( x*a <= 0 ) throw 666;
cout << "x*a = " << x*a << ", x+b = " << x+b << endl;

if( x*a <= x+b )
{
fire_nukes_at( x+b ); // Safe! Guaranteed x+b > 0! Yay!
}
else
{
cout << "Oh thank Odin! The war is over!" << endl;
// Massive sillycode here may be optimized away, or ... ?
}
}

int main()
{
foo<1, 1>( unsigned(-1)/2 );
}
[code]

I use a platform-specific construct in "main", since it works for all
platforms I would ever need to support (not Univac), just to show how
silly it is to abstain from everything not guaranteed by the standard,
and to throw a bone to those dogs who would like to make some noise.

Anyway, compiling with Visual C++ 11.0 (that's Visual C++ 2012, and also
internal version number 17.00, as you see reported below), where option
"/Ox" requests maximum optimization:

[example]
[D:\dev\test]
> version cl
Microsoft (R) C/C++ Optimizing Compiler Version 17.00.60610.1 for x86

[D:\dev\test]
> cl foo.cpp /Ox /Feb
foo.cpp

[D:\dev\test]
> b
x*a = 2147483647, x+b = -2147483648
Oh thank Odin! The war is over!

[D:\dev\test]
> _
[/example]

Hm, it yielded wrap-around behavior.

In one (important?) sense that's pretty nice, because it avoided having
the "if" body executed with its condition indicating something untrue,
which could then cause nuclear missiles to be FIRED ON OURSELVES.

But anyway, what does MinGW g++ do?

I'm no wizard with g++ options, so maybe the example below lacks the
specific option that will cause a bit of nuclear fireworks.

I'm just hoping "-ofast" will suffice to get that awfully expensive and
unacceptable integer comparison in the "if" condition, removed:

[example]
[D:\dev\test]
> version g++
g++ (GCC) 4.7.2

[D:\dev\test]
> g++ foo.cpp -fno-wrapv -Ofast

[D:\dev\test]
> a
x*a = 2147483647, x+b = -2147483648
Oh thank Odin! The war is over!

[D:\dev\test]
> _
[/code]

Oh dear also g++ produced wrap-around behavior.

Which means that changing the standard in that direction would not
introduce any new inefficiency (assuming for the sake of discussion that
it is an inefficiency, which I'm not sure I agree with), but instead
capture existing practice, at least for these two compilers.


Cheers & hth.,

- Alf (reality inspector)

Alf P. Steinbach

unread,
Oct 11, 2013, 10:16:48 AM10/11/13
to
On 11.10.2013 11:00, David Brown wrote:
>
> What are you going to moan about next? That gcc doesn't come with a
> good editor?
>

Your comments here are inane.

- Alf

Alf P. Steinbach

unread,
Oct 11, 2013, 10:19:50 AM10/11/13
to
On 11.10.2013 11:09, David Brown wrote:
> On 10/10/13 22:58, Alf P. Steinbach wrote:
>> On 10.10.2013 22:34, Tobias Müller wrote:
>>>
>>> I don't fully understand the for-loop example on that page, but it
>>> seems to enable various loop optimizations.
>>
>> It's good that you don't understand it, because the description is
>> fallacious and wrong. In particular, for the example
>>
>> for (i = 0; i <= N; ++i) { ... }
>>
>> the description "if the variable [namely 'i'] is defined to wrap around
>> on overflow, then the compiler must assume that the loop is possibly
>> infinite" is false. The author may be thinking of the case where N is
>> the maximum signed value, but that's easy to check for, and even easier
>> for the programmer to specify if e.g. loop unrolling is desired.
>
> It is quite simple - "N" is not a compile-time constant. The compiler
> may be able to generate better code knowing that the loop will always
> end and that "i" will always be non-negative (assuming undefined
> behaviour for integer overflow), than it could do without that assumption.

The compiler can always (emit code to) split the cases of terminating
and infinite loop, so you're wrong about that, but I think you're RIGHT
about the possibly better code when the compiler can assume that "i" is
non-negative.

I can't think of an example right here and now, but more constraints
generally do help the optimizer.

However that's not what is claimed on the referred page, and quoted above.

- Alf

David Brown

unread,
Oct 11, 2013, 10:43:53 AM10/11/13
to
On 11/10/13 16:03, Alf P. Steinbach wrote:

<snip code>
I've just tested gcc (version 4.5.1) with your code. The only change I
made was using "const char*" instead of "auto const", because my gcc is
a little older. And I have no "-Ofast", so tested with -O2 and -Os:


$ g++ t2.cpp -o t2 -Os -Wall && ./t2
x*a = 2147483647, x+b = -2147483648
Oh thank Odin! The war is over!

$ g++ t2.cpp -o t2 -O2 -Wall && ./t2
t2.cpp: In function ‘void foo(int) [with int a = 1, int b = 1]’:
t2.cpp:30:31: instantiated from here
t2.cpp:17:5: warning: assuming signed overflow does not occur when
assuming that (X + c) >= X is always true
x*a = 2147483647, x+b = -2147483648
Firing nukes at ourselves!



We are talking about /undefined behaviour/ here. If you want to check
reality, you will have to do /far/ better than a single check of a
couple of compilers on one platform.

Give me test results from a dozen code cases in each of C and C++,
compiled with multiple compilers (at least MSVC, gcc, llvm and Intel),
with several versions, on at least Windows and Linux, each with 32-bit
and 64-bit targets, and each with a wide variety of command line
switches. Show me that /everything/ except gcc /always/ treats signed
overflow as modular, and I start to consider that there might be a pattern.

Then I'll send you to check perhaps 40 different embedded compilers for
30 different targets.

Alternatively, I'll settle for quotations from the documentation for
said compilers guaranteeing this behaviour, if you don't want to test
them all.


Öö Tiib

unread,
Oct 11, 2013, 10:56:44 AM10/11/13
to
Reading this thread and seeing the flame war and thinking I suddenly
realized that it is not fault of gcc it has such overabundant
opportunities to joke in situations. May be it even has to joke indeed
on UB to get best performance on non-UB. So ... it is fault of the
language.

Why integer arithmetic of C++ does not ever stop of being that
"everything is UB" nonsense? Lot more logical would be to exactly define
what will or will not happen if:
1) std::numeric_limits<int>::is_modulo is true
2) std::numeric_limits<int>::has_quiet_NaN is true
3) std::numeric_limits<int>::traps is true
etc.

Otherwise what is the point to have such verbose crap like
'std::numeric_limits' in library if those constexpr bools have outright
zero behavioral connection to actual arithmetic done (or not done or
optimized out)?

I see that for example integer arithmetic with 'has_quiet_NaN' would
open up whole pile of additional optimization and arithmetic with
'is_modulo' would turn that 'a < a + 1 -: true' off by definition.

C++ can even let us to decide ourselves what to do for example on
arithmetic overflow. It looks cheap enough, clear enough, easily
configurable and beneficial to performance. Like gcc thankfully already
does with that -fwrapv.

So why not?

Alf P. Steinbach

unread,
Oct 11, 2013, 5:10:36 PM10/11/13
to
> I've just tested gcc (version 4.5.1) with your code. The only change I
> made was using "const char*" instead of "auto const", because my gcc is
> a little older. And I have no "-Ofast", so tested with -O2 and -Os:
>
>
> $ g++ t2.cpp -o t2 -Os -Wall && ./t2
> x*a = 2147483647, x+b = -2147483648
> Oh thank Odin! The war is over!
>
> $ g++ t2.cpp -o t2 -O2 -Wall && ./t2
> t2.cpp: In function ‘void foo(int) [with int a = 1, int b = 1]’:
> t2.cpp:30:31: instantiated from here
> t2.cpp:17:5: warning: assuming signed overflow does not occur when
> assuming that (X + c) >= X is always true
> x*a = 2147483647, x+b = -2147483648
> Firing nukes at ourselves!

Thanks for testing that :-).

Using -O2 makes no difference here :-(, with my main g++ installation,
but it's nice to see the issue for real.

Note that execution of the "if" body with a "false" "if" condition that
is assumed by the programmer to hold in the "if" body, caused
self-immolation -- broken assumptions generally do wreak havoc.

Also note that the behavior is different with optimization (release
build) and without (which can be the case for a debug build), with no
apparent problem for the debug build.

So the gcc optimization yields

(1) broken assumptions, perhaps thereby causing a nuclear attack on
one's own position, and

(2) possibly/probably active sabotage of debugging efforts to fix that,
with no problem apparent in the debugging

which nastiness IMHO is a pretty steep cost for avoiding one or two
machine code instructions in a rare special case.


> We are talking about /undefined behaviour/ here.

Yes, that's what this sub-thread is about and has been about, a special
case of formal UB.

Good observation.

And with well-defined signed arithmetic -- modular -- one would have
avoided that UB.

And one would then therefore also avoid the broken assumption, and
therefore also the resulting self-immolation or other unplanned effect.

As mentioned earlier, and I think you agreed with that, formal UB is not
a carte blanche for the compiler to do unreasonable things, such as
removing the function call in "x = foo( i++, i++ )", or removing whole
loops and, except when the programmer tries to find out what's going on
by debugging, executing "if" bodies when their conditions don't hold.


> If you want to check
> reality, you will have to do /far/ better than a single check of a
> couple of compilers on one platform.
>
> Give me test results from a dozen code cases in each of C and C++,
> compiled with multiple compilers (at least MSVC, gcc, llvm and Intel),
> with several versions, on at least Windows and Linux, each with 32-bit
> and 64-bit targets, and each with a wide variety of command line
> switches. Show me that /everything/ except gcc /always/ treats signed
> overflow as modular, and I start to consider that there might be a pattern.
>
> Then I'll send you to check perhaps 40 different embedded compilers for
> 30 different targets.

I believe those requirements are far more stringent than the reality
checking before the decision to make std::string's buffer guaranteed
contiguous, at the committee's meeting at Lillehammer in 2005.

However, while your requirements are unrealistic it's unclear what they
are requirements for.

Some others and I are discussing the issue of whether it might be a good
idea (or not) to make signed arithmetic formally modular. As I've shown
you by linking to Google Groups' archive, this is an old discussion in
clc++. I referred to a thread 9 years ago, but it goes even further
back: most arguments are well known; regarding this issue you have
contributed nothing new so far.

But your requirements statement above indicates that you are discussing
whether gcc is alone in its treatment of signed overflow.

I don't know, but I do think you're alone in discussing that.


> Alternatively, I'll settle for quotations from the documentation for
> said compilers guaranteeing this behaviour, if you don't want to test
> them all.

I'll just note now that with guaranteed modular signed arithmetic, one
would more probably add an assertion about e.g. "x+b > 0".

Such simple assertions would then not only most probably catch the
invalid actual argument, but the added knowledge could in many cases be
used by the compiler to optimize the code.

Of course, instead of CHANGING the semantics of existing types, one
could introduce new types, perhaps via a header like <stdint.h>.

In passing, new types are also IMO a good solution for gcc's problems
with IEEE conformance (it's of two minds because semantics changes with
options, and so it reports false guarantees via numeric_limits).

So, the general idea is a win-win-win: getting rid of UB, detecting bugs
up front, getting optimization without counter-intuitive cleverness --
and incidentally getting rgw same behavior for release and debug
builds of the code, which I think is very much desirable.


- Alf

Tobias Müller

unread,
Oct 11, 2013, 5:11:51 PM10/11/13
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
> On 11.10.2013 00:10, Tobias Müller wrote:
>> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
>>> On 10.10.2013 22:34, Tobias Müller wrote:
>>>> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
>>>>
>>>>> I do not know of any optimization of the general run time behavior of
>>>>> signed integer addition that come in addition to general optimizations
>>>>> like replacing "i = i + 1" with an increment instruction.
>>>>
>>>> x < x + 1 ==> true
>>>> x * 2 / 2 ==> x
>>>> (found here:
>>>> http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html?m=1)
>>>
>>> Yeah, this is what we've been talking about (so, obviously known), and
>>> no, these are not optimizations of the run time behavior.

Just for my understanding, what do you mean by "optimization of the runtime
behavior"? In the end, all optimizations are affect the runtime behavior
somehow, otherwise they would not be optimizations.

[...]
>>> So it's just wrong to optimize that, regardless of assumptions about the
>>> arithmetic. It certainly does not help any to optimize it. It's like,
>>> wrong no matter how you regard it, like, always wrong, you know.
>>
>> No, it is essential, especially in combination with templates.
>> In the generic form, the optimizations are usually not possible, but in a
>> specific instanciation they may be important.
>> Similar for inlining and whole program optimization.

Even if you try to ignore it, this is a major point and IMO outweights the
downsides, that only happen in bad code.

>>> More importantly, such aggressive and non-intuitive micro-optimizations
>>> that affect intended meaning and therefore correctness, can be TURNED ON
>>> instead of having to be turned off.
>>
>> They only affect the meaning of badly written code!
>
> So? How do you propose to avoid all such code?

If a programmer knowingly and carelessly relies on UB, I expect his code to
contain more flaws of the same kind that are not immediately visible.
Probably he also assumes a specific sizeof(int) for his undefined signed
integer overflow.

>>>> I don't fully understand the for-loop example on that page, but it seems to
>>>> enable various loop optimizations.
>>>
>>> It's good that you don't understand it, because the description is fallacious and wrong.
>>
>> Despite your experience, in this case I trust the llvm devs. Please don't
>> take this as a personal attack, it's not.
>
> It's OK, my explanation was just not clear enough. So, thanks for that feedback. ;-)
>
>
>>> In particular, for the example
>>>
>>> for (i = 0; i <= N; ++i) { ... }
>>>
>>> the description "if the variable [namely 'i'] is defined to wrap around
>>> on overflow, then the compiler must assume that the loop is possibly
>>> infinite" is false. The author may be thinking of the case where N is the
>>> maximum signed value, but that's easy to check for, and even easier for
>>> the programmer to specify if e.g. loop unrolling is desired.
>
> I omitted this sentence continuation for brevity in the quote above: "-
> which then disables these important loop optimization".
>
> Even if you disagree with the notion that "must assume" is false, clearly
> the bit about disabling the optimization is false.
>
> After all, they can easily be done, which contradicts that they cannot be done.
>
> Is that clear enough explanation?
>
> Oh well, to prove that for yourself if you don't see it, simply look up
> Duff's Device in Wikipedia (if you're not familiar with it), and
> hand-craft that optimization for a relevant loop with signed loop
> variable where N can possibly but necessarily be INT_MAX.

So loop unrolling is possible even in the case of an infinite loop, which
intuitively makes sense. I expect that the clang devs know that, so I guess
they had different optimizations in mind.

> So, even more importantly than the incorrect "must assume", the claim
> that this loop would be impossible to optimize is completely and utterly false.

Well, the compiler must assume, that there are cases where the loop does
not terminate. And if necessary handle them differently.

AFAIK, optimizations considered for -O2 usually don't trade binary size for
speed, so this would disable those optimization for the typical setting.

> And so, with errors and misdirections galore, all of them pointing in one
> direction, for the purely technical, for what you assume internally for
> your programming, it's NOT a good idea to trust that document.

Even if you don't agree with the reasoning, it is still helpful because it
points out many possible traps.

> Rather trust your instinct, that when you initially find that, as you
> wrote, you "don't fully understand" it, and it's about something simple,
> then there's most likely something fishy, something not right, and when
> written by intelligent people it's likely misdirection. :)

I don't think like that. Rather I try to understand it before I make up my
opinion. Even more in cases like this where the author is much more
involved in the matter than I am.

>> I hope you don't expect me to annotate my code with all desired
>> optimizations.
>
> No, that would be a rather impractical approach.
>
> Can you think of anything more practical?
>
>
>> The test if N == INT_MAX would still have to be done at runtime and handled
>> properly.
>
> No, but knowledge about N has to be established either at compile time or at run time.

The case where N is known at runtime is not interesting. That's an easy
target, regardless whether it is UB or modular.

> Note that this is the same as for unsigned loop variable.

I expect that there are optimizations where you don't need any runtime
information about N other than that the loop always terminates.

> Effectively, therefore, the author(s) pretend that people are using
> signed loop variables in order to allow gcc to silently exploit formal UB
> to optimize away two machine code instructions for the case where N is
> not known to be in reasonable range, and that it's important to have this
> micro-optimization invoked in that very subtle and implicit way.
>
> Which is just silly.
>
> At least when you reflect on it a bit.

If I reflect a bit on that example above, I come to the conclusion that an
infinite loop in one single corner case is certainly not expected behavior.
You are argumenting against your own principle of least surprise.

With UB, the loop works as expected even if you forget to validate N.

>> This means you have one optimized and one unoptimized
>> (superfluous) version of the loop in your binary.
>
> No, but that's the worst case.

"No, but..."?

> One far more PRACTICAL approach is that the compiler warns when the loop
> can be infinite and unrolling has been asked for; then it can be
> rewritten or an option supplied that says "assume finite loops".

And what do you do if you encounter that warning but know that you
validated N and it is safe? In the end all warnings should be eliminated.

> Same with unsigned loop variable. ;-)

Here the solution is: take a signed loop variable.

Tobi

Tobias Müller

unread,
Oct 11, 2013, 5:27:37 PM10/11/13
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
> On 11.10.2013 07:01, Paavo Helde wrote:
>> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
>> news:l3795i$ssq$1...@dont-email.me:
>>> And if 'x<x+1' is produced by template inlining then 'x' is presumably
>>> known at compile time, or do I misunderstand what you have in mind?
>>
>> No, this is not what I had in my mind. The type of x would be (one of)
>> template parameters, but the values are known only at run-time. I am mostly
>> concerned about array operations, where the type of array elements is not
>> fixed and the code must support all (numeric) types, for example.
>
> Perhaps you could give a CONCRETE EXAMPLE?
>
> Because I'm baffled as to how "x < x+1" could be produced, with x a
> variable and the original code meaningful.

inline void foo(int x, int y)
{
if (y < x + 1)
{
//...
}
}

int a = ...
f(a, a);

No template needed, inlining is enough.

Tobi

Rupert Swarbrick

unread,
Oct 11, 2013, 6:31:57 PM10/11/13
to
>> Oh well, to prove that for yourself if you don't see it, simply look up
>> Duff's Device in Wikipedia (if you're not familiar with it), and
>> hand-craft that optimization for a relevant loop with signed loop
>> variable where N can possibly but necessarily be INT_MAX.
>
> So loop unrolling is possible even in the case of an infinite loop, which
> intuitively makes sense. I expect that the clang devs know that, so I guess
> they had different optimizations in mind.

I don't know much about clang, but here's a couple of examples where GCC
cares, at least. GCC has different ways to try and optimise a tight
loop. Unrolling is one. Another is modulo scheduling, which can get
impressive performance gains without the code bloat you get with
unrolling. However, designing (or computing) a modulo schedule is really
rather hard, so GCC just gives up if the loop isn't a "simple loop". A
simple loop is defined to be one where you know at the start how many
times you're going to go around.

Another example where I might care about this is with a "zero overhead
loop" instruction. They tend to be of the form

loop some_register, some_label
do_stuff
some_label:

where some_register contains the number of iterations to do. Of course,
if the compiler can't work out how to put the number of iterations into
some_register in advance, it won't be able to use this.


Rupert

Leigh Johnston

unread,
Oct 11, 2013, 7:05:17 PM10/11/13
to
You are talking nonsense mate, it doesn't matter what the compiler does
if undefined behaviour is invoked. Why? Because undefined behaviour is a
bug and invoking it deliberately is a deliberate bug. Avoid undefined
behaviour like the plague.

/Leigh

Jorgen Grahn

unread,
Oct 12, 2013, 6:37:49 PM10/12/13
to
On Thu, 2013-10-10, Alf P. Steinbach wrote:
...
> g++ library status (this looks current):
>
> http://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.2011
>
> No regexp yet

It looks that way in the web page, but as I noted in a semi-recent
thread there's a lot of std regex code in the library and it has been
there for years. As far as I can tell the most useful parts are
probably there.

Perhaps someone who has actually /used/ it can comment? I haven't
written a line of C++11 yet; I'm still at TR1.

/Jorgen

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

David Brown

unread,
Oct 13, 2013, 4:48:34 PM10/13/13
to
If the programmer makes assumptions that are broken, then havoc will
occur. Other than that, your paragraph makes no sense to me.

> Also note that the behavior is different with optimization (release
> build) and without (which can be the case for a debug build), with no
> apparent problem for the debug build.

If you are using different options for releases from those for debugging
(other than early testing, or perhaps special case debugging) then your
development process is broken. Test what you ship, ship what you test.

>
> So the gcc optimization yields
>
> (1) broken assumptions, perhaps thereby causing a nuclear attack on
> one's own position, and

Let's be 100% clear here - it is the programmer's assumptions that are
broken. The compiler is correct.

So gcc's optimisation demonstrates that people who do not understand the
undefined behaviour of signed overflow in C and C++ are not qualified to
write nuclear attack code. I think we already knew that.

>
> (2) possibly/probably active sabotage of debugging efforts to fix that,
> with no problem apparent in the debugging

Are you /really/ suggesting that gcc optimises code according to the C
and C++ standards, and according to normal programmer's understandings,
as some sort of method of deliberately causing /you/ trouble during
debugging of invalid code?

>
> which nastiness IMHO is a pretty steep cost for avoiding one or two
> machine code instructions in a rare special case.
>

Yes, those evil gcc programmers are scheming away trying to find new
ways to annoy you.

>
>> We are talking about /undefined behaviour/ here.
>
> Yes, that's what this sub-thread is about and has been about, a special
> case of formal UB.

It is not "a special case" - it is one of many cases of undefined behaviour.

>
> Good observation.
>
> And with well-defined signed arithmetic -- modular -- one would have
> avoided that UB.

The standards committee could also have defined "1/0" to mean "send the
developer some flowers" - then it would have avoided another sort of
undefined behaviour. It might be tough to implement, but it would be
well defined.

We have already covered the fact that modular behaviour of signed
integers is counter-intuitive, and is at best one of many possible
choices for defining behaviour. Giving signed overflow a defined
behaviour will not make incorrect code correct - if you have code that
relies on a signed overflow, your code is broken.


So since modular signed integer behaviour does not help any programmer
(except perhaps people moving from Java to C++ and who like to write
awkward code), and it hinders valid optimisations that turn up as a
result of inlining, templates, and constant propagation, I think the gcc
developers have done a good job here.


And if you or anyone else insists on denying the reality of C's
undefined behaviour, then I recommend learning to enable warnings in gcc
so that the compiler will tell you when you are being stupid (at least,
in this particular case).


>
> And one would then therefore also avoid the broken assumption, and
> therefore also the resulting self-immolation or other unplanned effect.
>
> As mentioned earlier, and I think you agreed with that, formal UB is not
> a carte blanche for the compiler to do unreasonable things, such as
> removing the function call in "x = foo( i++, i++ )", or removing whole
> loops and, except when the programmer tries to find out what's going on
> by debugging, executing "if" bodies when their conditions don't hold.

Yes, undefined behaviour /is/ a carte blanche for the compiler to do
such things. It is very simple to avoid writing undefined behaviour -
learn to use the programming language you are working with. As an aid,
learn to use the tools you are working with - gcc will warn about lots
of undefined behaviour.

I don't care if "x = foo(i++, i++)" gets removed, or translated into
"format c:". I would not write it - nor would anyone else with even a
basic understanding of C - other than perhaps to test the compiler. And
even if I /did/ write it as a typo, the compiler would tell me it was a
bad idea.

>
>
>> If you want to check
>> reality, you will have to do /far/ better than a single check of a
>> couple of compilers on one platform.
>>
>> Give me test results from a dozen code cases in each of C and C++,
>> compiled with multiple compilers (at least MSVC, gcc, llvm and Intel),
>> with several versions, on at least Windows and Linux, each with 32-bit
>> and 64-bit targets, and each with a wide variety of command line
>> switches. Show me that /everything/ except gcc /always/ treats signed
>> overflow as modular, and I start to consider that there might be a
>> pattern.
>>
>> Then I'll send you to check perhaps 40 different embedded compilers for
>> 30 different targets.
>
> I believe those requirements are far more stringent than the reality
> checking before the decision to make std::string's buffer guaranteed
> contiguous, at the committee's meeting at Lillehammer in 2005.
>
> However, while your requirements are unrealistic it's unclear what they
> are requirements for.

You claim that gcc is somehow perverse and awkward about optimising
unsigned overflow as undefined behaviour. If you want to be taken
seriously in that claim, show us that a substantial proportion of other
compilers have a different behaviour. I think that is only reasonable.

>
> Some others and I are discussing the issue of whether it might be a good
> idea (or not) to make signed arithmetic formally modular. As I've shown
> you by linking to Google Groups' archive, this is an old discussion in
> clc++. I referred to a thread 9 years ago, but it goes even further
> back: most arguments are well known; regarding this issue you have
> contributed nothing new so far.

And fortunately the powers that be in the C and C++ world have done the
right thing - ignored you. It is a silly idea.

>
> But your requirements statement above indicates that you are discussing
> whether gcc is alone in its treatment of signed overflow.
>
> I don't know, but I do think you're alone in discussing that.

You started this whole thread by claiming that gcc was perverse and
intentionally awkward by its treatment of signed overflow!

"In practice it's modular on all modern systems, but at least one
compiler (namely g++) uses the formal UB for overflow, supporting
archaic systems, as an excuse to do rather inconvenient things, like
"optimizations". You will most proabbly stop g++ from doing that by
using the option "-fwrapv". This informs that compiler that you do want
the practical and efficient machine code level modular arithmetic
behaviour, two's complement form."


>
>
>> Alternatively, I'll settle for quotations from the documentation for
>> said compilers guaranteeing this behaviour, if you don't want to test
>> them all.
>
> I'll just note now that with guaranteed modular signed arithmetic, one
> would more probably add an assertion about e.g. "x+b > 0".

Marvellous - you want to give your own specific definition to the
undefined behaviour, and to make it work safely you want to add extra
source code (with no logical meaning) that will (at least sometimes,
depending on what the compiler can figure out at compile time) mean more
run-time code.

>
> Such simple assertions would then not only most probably catch the
> invalid actual argument, but the added knowledge could in many cases be
> used by the compiler to optimize the code.
>

Sometimes assertions can be useful for that sort of thing - such as for
checking that your values are within the limits of defined behaviour.

> Of course, instead of CHANGING the semantics of existing types, one
> could introduce new types, perhaps via a header like <stdint.h>.
>
> In passing, new types are also IMO a good solution for gcc's problems
> with IEEE conformance (it's of two minds because semantics changes with
> options, and so it reports false guarantees via numeric_limits).
>
> So, the general idea is a win-win-win: getting rid of UB, detecting bugs
> up front, getting optimization without counter-intuitive cleverness --
> and incidentally getting rgw same behavior for release and debug
> builds of the code, which I think is very much desirable.
>

Learn to use "-Wall". That gets rid of this particular undefined
behaviour, along with many other types of undefined behaviour or other
code errors.

Learn that "optimising" is something a compiler can /always/ do,
regardless of the flags you give it.

Learn to use a proper development process. Then you will not have
"release" and "debug" builds, and therefore no inconsistencies no matter
how badly you understand the concept of undefined behaviour.

David Brown

unread,
Oct 13, 2013, 5:16:13 PM10/13/13
to
On 11/10/13 16:19, Alf P. Steinbach wrote:
> On 11.10.2013 11:09, David Brown wrote:
>> On 10/10/13 22:58, Alf P. Steinbach wrote:
>>> On 10.10.2013 22:34, Tobias Müller wrote:
>>>>
>>>> I don't fully understand the for-loop example on that page, but it
>>>> seems to enable various loop optimizations.
>>>
>>> It's good that you don't understand it, because the description is
>>> fallacious and wrong. In particular, for the example
>>>
>>> for (i = 0; i <= N; ++i) { ... }
>>>
>>> the description "if the variable [namely 'i'] is defined to wrap around
>>> on overflow, then the compiler must assume that the loop is possibly
>>> infinite" is false. The author may be thinking of the case where N is
>>> the maximum signed value, but that's easy to check for, and even easier
>>> for the programmer to specify if e.g. loop unrolling is desired.
>>
>> It is quite simple - "N" is not a compile-time constant. The compiler
>> may be able to generate better code knowing that the loop will always
>> end and that "i" will always be non-negative (assuming undefined
>> behaviour for integer overflow), than it could do without that
>> assumption.
>
> The compiler can always (emit code to) split the cases of terminating
> and infinite loop, so you're wrong about that,

If the compiler knows that the loop will terminate or not in advance,
then it does not need to emit extra code - adding code to check the
particular case, then two types of loop code. Clearly this is better
than doing the check at run time - so I was right here too.

> but I think you're RIGHT
> about the possibly better code when the compiler can assume that "i" is
> non-negative.
>
> I can't think of an example right here and now, but more constraints
> generally do help the optimizer.
>
> However that's not what is claimed on the referred page, and quoted above.
>

The page says:

"On the other hand, if the variable is defined to wrap around on
overflow, then the compiler must assume that the loop is possibly
infinite (which happens if N is INT_MAX) - which then disables these
important loop optimizations."

This is perfectly true - the compiler /must/ assume that the loop is
possibly infinite. You could argue that the compiler does not have to
"disable these important loop optimisations" - it could, as you suggest,
make duplicate code. But that means possibly a lot of extra code to
deal with a case that will almost certainly never happen (N being
INT_MAX) and is almost certainly not intended by the programmer. The
author says that the compiler /does/ disable these optimisations, not
that it /must/.

So the author is correct here too.

Alf P. Steinbach

unread,
Oct 13, 2013, 5:41:10 PM10/13/13
to
Programmers, although perhaps not you, are fallible and make mistakes
all the time.

However, you are conflating

* the assumption of the programmer making a call with invalid argument
value, with

* the assumption of the programmer coding up a function as if an "if"
block would only be executed when its condition holds, i.e. just
assuming a reasonable compiler.

The first point is about the kind of mistakes programmers make all the
time, and that they are not surprised to discover does not hold.

The second point is different.

One is pretty surprised when that happens, when the control flow is
other than specified in the program text.

I suspect that your conflation of these issues, of what the programmer
does versus what the compiler does, is intentional, because it's near
impossible to write coherent text yet make incorrect and misleading
conclusions about just every issue, consistently over time, as you have
done.

Anyway, it's easy to support the programmers, and it's also easy to make
their work perversely difficult, and gcc goes the second route.

With modular arithmetic as standard one avoid all that.


>> Also note that the behavior is different with optimization (release
>> build) and without (which can be the case for a debug build), with no
>> apparent problem for the debug build.
>
> If you are using different options for releases from those for debugging
> (other than early testing, or perhaps special case debugging) then your
> development process is broken.

If the debug and release build options are identical, then there's no
difference between debug and release builds.

Hence your statement is meaningless, balderdash, rubbish.

I can't imagine what kind of reader you're writing for.


> Test what you ship, ship what you test.

That's good advice, but very misleading in context -- you're implying
that someone had argued differently.


>> So the gcc optimization yields
>>
>> (1) broken assumptions, perhaps thereby causing a nuclear attack on
>> one's own position, and
>
> Let's be 100% clear here - it is the programmer's assumptions that are
> broken. The compiler is correct.

And so, when a person hurts himself or others with a power tool that
instead of safety features has error amplification, you refuse to admit
that the tool might be improved.

It's the (imperfect) user's fault, yeah.

That's dumb.


> So gcc's optimisation demonstrates that people who do not understand the
> undefined behaviour of signed overflow in C and C++ are not qualified to
> write nuclear attack code. I think we already knew that.
>
>>
>> (2) possibly/probably active sabotage of debugging efforts to fix that,
>> with no problem apparent in the debugging
>
> Are you /really/ suggesting that gcc optimises code according to the C
> and C++ standards, and according to normal programmer's understandings,
> as some sort of method of deliberately causing /you/ trouble during
> debugging of invalid code?

From here on and onward you're arguing AS IF your opponent engages in
dangerous habits, doesn't understand basics, etc. ad nauseam, and you do
that even after having been proven wrong about a number of basic
technical issues, and after displaying lack of knowledge about history,
terms, standards, etc. -- and after unsuccessfully having tried that
"discredit" tactic a number of times, and been caught at it.

That's also a pretty dumb thing to do, sorry.


[snip a lot more like that]

- Alf

Öö Tiib

unread,
Oct 13, 2013, 5:47:02 PM10/13/13
to
On Sunday, 13 October 2013 23:48:34 UTC+3, David Brown wrote:
> We have already covered the fact that modular behaviour of signed
> integers is counter-intuitive, and is at best one of many possible
> choices for defining behaviour.

Implementation /has/ to specialize 'std::numeric_limits<int>'.
It has 'is_modulo', 'has_quiet_NaN', 'traps' and pile of others. So
why do those things only affect inline assembler? Why there are no
relation between arithmetic of numeric types and traits of the very
same numeric types? Why there has to be that dreaded UB?

David Brown

unread,
Oct 13, 2013, 8:02:27 PM10/13/13
to
That's true. But it is not the compiler's job to hand-hold for simple
things.

Remember, in this particular case the programmer has to be well versed
in the low-level implementation of computer arithmetic to even consider
the idea of modular behaviour of signed overflow - it is not a natural
idea for a beginner. So you are talking about programmers who have a
good understanding of low-level integer arithmetic, yet a poor
understanding of C's behaviour, an inability to use compiler warnings,
and a total lack of ability to look things up on the internet. That's a
small subset of programmers.


>
> However, you are conflating
>
> * the assumption of the programmer making a call with invalid argument
> value, with
>
> * the assumption of the programmer coding up a function as if an "if"
> block would only be executed when its condition holds, i.e. just
> assuming a reasonable compiler.

You are making absolutely no sense.

>
> The first point is about the kind of mistakes programmers make all the
> time, and that they are not surprised to discover does not hold.
>
> The second point is different.
>
> One is pretty surprised when that happens, when the control flow is
> other than specified in the program text.
>
> I suspect that your conflation of these issues, of what the programmer
> does versus what the compiler does, is intentional, because it's near
> impossible to write coherent text yet make incorrect and misleading
> conclusions about just every issue, consistently over time, as you have
> done.

You are still making absolutely no sense.

I get the vague feeling that you still think the programmer should be
able to write code that relies on undefined behaviour, but because /you/
think this case should be defined, the compiler should generate code
matching /your/ definition of the undefined behaviour - all because you
believe that /your/ definition is "reasonable", and therefore doing
anything else is "unreasonable".

>
> Anyway, it's easy to support the programmers, and it's also easy to make
> their work perversely difficult, and gcc goes the second route.
>
> With modular arithmetic as standard one avoid all that.
>

Modular signed overflow would be "perversely difficult" for anyone
expecting different behaviour from /your/ personal opinion.

Can you really not understand this?

Even if it were the case that the optimisations gcc performs here are so
minor that they can be ignored, defining signed overflow as modular is
logically meaningless and counter-intuitive.

Perhaps you have been programming in Java too much - the Java designers
went out of their way to avoid undefined behaviour by giving things a
definition, no matter how inconvenient. Fortunately the C designers
were smarter.

>
>>> Also note that the behavior is different with optimization (release
>>> build) and without (which can be the case for a debug build), with no
>>> apparent problem for the debug build.
>>
>> If you are using different options for releases from those for debugging
>> (other than early testing, or perhaps special case debugging) then your
>> development process is broken.
>
> If the debug and release build options are identical, then there's no
> difference between debug and release builds.
>

No - there is no "release" and "debug" build. There is only a "release"
build (plus any other temporary ones to aid early development or
specific test setups). That is the whole point - you should never have
a "release" and "debug" build setup. It's a common mistake, but it is
still a mistake.

> Hence your statement is meaningless, balderdash, rubbish.
>
> I can't imagine what kind of reader you're writing for.

Apparently I am writing for one that is /really/ slow at understanding
new concepts.

>
>
>> Test what you ship, ship what you test.
>
> That's good advice, but very misleading in context -- you're implying
> that someone had argued differently.
>

Yes, /you/ argued differently when you said you had different settings
for a "release" build and a "debug" build.

>
>>> So the gcc optimization yields
>>>
>>> (1) broken assumptions, perhaps thereby causing a nuclear attack on
>>> one's own position, and
>>
>> Let's be 100% clear here - it is the programmer's assumptions that are
>> broken. The compiler is correct.
>
> And so, when a person hurts himself or others with a power tool that
> instead of safety features has error amplification, you refuse to admit
> that the tool might be improved.
>
> It's the (imperfect) user's fault, yeah.
>
> That's dumb.

If a person is stupid enough to operate that power tool in the rain,
then it is the user's fault. This way the rest of the world does not
have to pay extra for power tools that are made waterproof.

Öö Tiib

unread,
Oct 13, 2013, 8:47:11 PM10/13/13
to
On Monday, 14 October 2013 03:02:27 UTC+3, David Brown wrote:
> No - there is no "release" and "debug" build. There is only a "release"
> build (plus any other temporary ones to aid early development or
> specific test setups). That is the whole point - you should never have
> a "release" and "debug" build setup. It's a common mistake, but it is
> still a mistake.

Really? All debuggers I have seen fail to match optimized programs back
to variables and original code so that pretty much makes debuggers useless
and debugging several times more expensive.

Unoptimized code with heavy usage of templates however is often performing
worse than similar code on Python interpreter.

So the "common mistake" seems to be winning strategy here. :D

Alf P. Steinbach

unread,
Oct 13, 2013, 9:47:03 PM10/13/13
to
The gcc optimization here, causing an "if"-block to be executed when its
condition does not hold, is IMHO not so simple.

Rather, it's subtle and very unexpected.

One does not expect an "if" block to be executed when it condition does
not hold, as tested or relied on within the block.


> Remember, in this particular case the programmer has to be well versed
> in the low-level implementation of computer arithmetic to even consider
> the idea of modular behaviour of signed overflow

Contradicting your immediately preceding statement, above.

I did expect a self-contradiction sooner or later, but still.

Ordinarily there is some distance between the self-contradictory statements.

Anyway, in addition to the self-contradiction you're again conflating
things, and adding very misleading loaded descriptions like "low level":
ordinary + is what one uses, it's not "low level".

And just to top it off, in addition to self-contradiction and misleading
characterizations, if it were true that signed arithmetic is too
difficult to understand, then you're saying that gcc is optimizing away
the ability to reliably detect errors for a case where you think the
programmer has no idea that there can be an error.


> - it [wrap-around] is not a natural idea for a beginner.

Any halfway decent C or C++ programmer knows about wrap-around for
signed arithmetic.

Even my young pupils at a vocational school in the early 1990's
understood this, then using Turbo Pascal and x86 assembly, and I would
absolutely not let them loose on modern C++.

In short, you're wrong.


> So you are talking about programmers who have a
> good understanding of low-level integer arithmetic, yet a poor
> understanding of C's behaviour, an inability to use compiler warnings,
> and a total lack of ability to look things up on the internet. That's a
> small subset of programmers.

Oh, that devolved fast.

"good understanding" is right and OK.

"low-level" is actively misleading, because the built-in arithmetic such
as + is the arithmetic one relates to and uses directly, and so there is
nothing particularly low level about that: it is not low level compared
to ordinary arithmetic in C++, i.e. compared to itself.

"C's behavior" is incorrect and misleading. It (that is, your issue, not
mine) is about the gcc compiler's aggressive default optimization, and
we're discussing C++ in C++ group. You have yourself shown that all
that's needed for the optimizaton to kick in, if one has a g++
installation/variant that does this, is a general "-O2" optimization.

"inability to use compiler warnings" is incorrect and misleading. In
many situations code that is maintained or used causes numerous
warnings. I prefer zero diagnostics code, but in many cases that's pure
fantasy, as is your idea of the perfect infallible programmer.

"lack of ability to look up" is again just misleading, and derogatory.
The programmer who does not know about the issue, or rather the issues,
does not know what to look up. Or that there is anything to look up.


>> However, you are conflating
>>
>> * the assumption of the programmer making a call with invalid argument
>> value, with
>>
>> * the assumption of the programmer coding up a function as if an "if"
>> block would only be executed when its condition holds, i.e. just
>> assuming a reasonable compiler.
>
> You are making absolutely no sense.

Oh dear.

[snipped further confused stuff, misdirections, etc.]


- Alf

Paavo Helde

unread,
Oct 14, 2013, 1:54:30 AM10/14/13
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
news:l3fif3$hq9$1...@dont-email.me:
>
> Any halfway decent C or C++ programmer knows about wrap-around for
> signed arithmetic.

How can they "know" something which does not hold? At most they might
have a wide-spread misconception.

> Even my young pupils at a vocational school in the early 1990's
> understood this, then using Turbo Pascal and x86 assembly, and I would
> absolutely not let them loose on modern C++.

Ah, it seems now we come to the crux of the point. You are conflating C++
and x86. Indeed, x86 has the same opcode for both signed and unsigned
addition, so it has no alternative other than to behave identically for
both. However, this does not mean that the same must hold for a high-
level language like C or C++, as has been indeed demonstrated by concrete
examples from gcc in this very thread.

Note that x86 raises the CPU overflow flag (OF) if there was an overflow.
The C and C++ implementations on x86 just chose to ignore this flag for
signed arithmetics overflow. We would be in a much better condition now
if they had chosen to trap it and abort the program, at least in "debug"
build mode or whatever. We would not have this pointless thread, and
there would hopefully be much less x86 assembler programmers having
misconceptions about C++.

Cheers
Paavo

Stuart

unread,
Oct 14, 2013, 2:11:37 AM10/14/13
to
On 10/08/13, David Brown wrote:
>> Most people assume "int" works like a mathematical integer - not
>> modular arithmetic.


On 10/08/13, Alf Steinbach wrote:
> Hm, don't know about "most people".


Well, I consider myself a pretty decent C++ programmer, but in my ten
years of professional C++ programming I never was in need of modular
arithmetic. Quite the contrary, I used to miss Ada95's ability to define
arithmetic types with upper and lower bounds (can sure be done with C++,
too, but that is not "built in" and thus there is no standard way of
doing it).


> No, all my reviews, when I worked (and now that I'm almost
> fully healed from surgeries I may just start working again,
> if employers are not scared away by long absence), said that
> I was extremely competent, not the opposite.


Oh gosh, I hope that you are alright. I was starting to wonder what
happened to you (didn't see you in this newsgroup). I can't imagine that
you should have trouble finding a decent job. After all, all you have to
do is telling them that they should have a look all your numerous
postings in this very newsgroup.


Regards and all the best wishes,
Stuart

David Brown

unread,
Oct 14, 2013, 5:44:32 AM10/14/13
to
Debugging effectively is always a bit of an art. Different types of
program, different types of problem, and different types of compiler
need different strategies. There are certainly times when you want to
disable (or lower) optimisations to aid debugging.

My point is that you do /not/ want to do your testing and debugging with
different settings from your release code. (I know that "testing" and
"debugging" are different things, but they are often done at least
partly together.)

Ian Collins

unread,
Oct 14, 2013, 5:54:33 AM10/14/13
to
Release testing maybe, but testing during development will often be done
with different settings and even (especially for embedded targets) on a
different platform.

--
Ian Collins

David Brown

unread,
Oct 14, 2013, 7:18:54 AM10/14/13
to
On 14/10/13 11:54, Ian Collins wrote:
> David Brown wrote:
Yes, as I noted earlier - at least during early development or for
looking at specific problems. By the time you are testing the whole
thing, you should be using the same settings as your final release will use.


Leigh Johnston

unread,
Oct 14, 2013, 10:14:58 AM10/14/13
to
Alf is just being an intransigent cock as usual.

It is simple: do not rely on signed overflow behaviour as such behaviour
is undefined as far as C++ is concerned.

/Leigh

Öö Tiib

unread,
Oct 14, 2013, 11:29:48 AM10/14/13
to
On Monday, 14 October 2013 14:18:54 UTC+3, David Brown wrote:
> On 14/10/13 11:54, Ian Collins wrote:
> > David Brown wrote:
So please clarify where is that "common mistake" that Alf made that you
noted earlier:

Maintenance of most products is sort of constant process since everything
contains subtle shortcomings. Often less skilled developers are used as
maintainers. More skilled are needed as writers of shiny new stuff.
Maintenance causes often new defects. Tester tests with "release" build.
Maintainer debugs findings of tester with "debug" build. If the two behave
differently then there may be additional clarification needed. That
possibly takes whole additional maintenance cycle. Finally maintainer has
to debug with "release" build to find the cause of issue. That is
additional inconvenience for anyway less skilled developer.

Alf P. Steinbach

unread,
Oct 14, 2013, 12:30:18 PM10/14/13
to
On 14.10.2013 07:54, Paavo Helde wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
> news:l3fif3$hq9$1...@dont-email.me:
>>
>> Any halfway decent C or C++ programmer knows about wrap-around for
>> signed arithmetic.
>
> How can they "know" something which does not hold?

All known extant C++ compilers (disregarding rumors of a Z-system
compiler) give wrap-around behavior for signed arithmetic, even though
this behavior is not mandated by the standard.

That's because the underlying hardware yields this behavior.

And so this includes, of course, gcc, as has been demonstrated directly
in this thread, but for you, here's a second example using g++ (gcc):

[code]
#include <iostream>
#include <limits.h>
using namespace std;

auto main() -> int
{
int a = -1/2u;
int b = a + 1;

cout << a << " " << b << endl;
}
[/code]

Compiling and running the program, in Windows:

[example]
[D:\dev\test]
> version g++
g++ (GCC) 4.7.2

[D:\dev\test]
> g++ foo.cpp

[D:\dev\test]
> a
2147483647 -2147483648

[D:\dev\test]
> _
[/code]

It's unclear what your vague "does not hold" is intended to mean, but
from the context I gather that you think the above example, and like
examples for as good as all (or perhaps just simply all) C++ compilers,
are made-up by folks like me in order to deceive you?

Anyway, if you know of an exception, a C or C++ compiler that does not
have two's complement signed integers, then do provide a reference.


> At most they might
> have a wide-spread misconception.

Perhaps you're conflating the formal guarantees of the standard with the
in-practice, as a certain Herb Schildt did regarding this matter
(although his point of view was the opposite of yours)?


>> Even my young pupils at a vocational school in the early 1990's
>> understood this, then using Turbo Pascal and x86 assembly, and I would
>> absolutely not let them loose on modern C++.
>
> Ah, it seems now we come to the crux of the point. You are conflating C++
> and x86.

No, you got both history and technology wrong here.

Wrap-around behavior for signed arithmetic is a consequence of the
generally simplest and most efficient way to implement signed arithmetic
in hardware.

It's also mathematically the simplest, which is no coincidence.

Thus it was widely used on computers before the emergence of the i8086
in, IIRC, 1979.

And the C language goes back some years before that.


[snip more such stuff]


> Note that x86 raises the CPU overflow flag (OF) if there was an overflow.
> The C and C++ implementations on x86 just chose to ignore this flag for
> signed arithmetics overflow.

Not only on x86, but as a pretty much universal convention.

The major reason has to do with the purpose of the language.



> We would be in a much better condition now
> if they had chosen to trap it and abort the program, at least in "debug"
> build mode or whatever.

Well, given that you have your facts wrong (see above), your evaluation
of good versus bad here is better ignored. That doesn't mean that its
necessarily wrong or unfounded, but your comments so far, instead of
building a case for someone knowledgeable enough to hold an opinion on
this, has built the case for the opposite, that, in spite of offering
some facts, you don't know the basics of the subject matter. Sorry.

Alf P. Steinbach

unread,
Oct 14, 2013, 12:38:51 PM10/14/13
to
On 14.10.2013 08:11, Stuart wrote:
> On 10/08/13, David Brown wrote:
>>> Most people assume "int" works like a mathematical integer - not
>>> modular arithmetic.
>
>
> On 10/08/13, Alf Steinbach wrote:
>> Hm, don't know about "most people".
>
>
> Well, I consider myself a pretty decent C++ programmer, but in my ten
> years of professional C++ programming I never was in need of modular
> arithmetic.

Right. It's needed mostly for some (important) corner cases. But I'm
sure that contrary to David Brown's opinion about C++ programmers in
general, that in his view they're ignorant of even such basic matters,
you did KNOW about it -- in particular since modular arithmetic is
guaranteed by the C++ standard for unsigned types.


> Quite the contrary, I used to miss Ada95's ability to define
> arithmetic types with upper and lower bounds (can sure be done with C++,
> too, but that is not "built in" and thus there is no standard way of
> doing it).
>
>
> > No, all my reviews, when I worked (and now that I'm almost
> > fully healed from surgeries I may just start working again,
> > if employers are not scared away by long absence), said that
> > I was extremely competent, not the opposite.
>
>
> Oh gosh, I hope that you are alright. I was starting to wonder what
> happened to you (didn't see you in this newsgroup). I can't imagine that
> you should have trouble finding a decent job. After all, all you have to
> do is telling them that they should have a look all your numerous
> postings in this very newsgroup.

Thanks. :)

Just note, that since the influx of small trolls in this NG there are
probably a number of readers who thing you're being ironic, and who
think that scoring in such a way would be a good argument in a technical
debate.

They would not understand the word "fallacy" it it bit them in the ass.

Paavo Helde

unread,
Oct 14, 2013, 12:57:55 PM10/14/13
to
嘱 Tiib <oot...@hot.ee> wrote in
news:26715ece-ab3f-4cc6...@googlegroups.com:

> Maintenance causes often new defects. Tester tests with
> "release" build. Maintainer debugs findings of tester with "debug"
> build. If the two behave differently then there may be additional
> clarification needed. That possibly takes whole additional maintenance
> cycle. Finally maintainer has to debug with "release" build to find
> the cause of issue. That is additional inconvenience for anyway less
> skilled developer.

In my experience bugs which appear in release, but not in debug, are very
rare. Even if they are much harder to fix, it does not outweigh the
convenience of solving the rest 99.9% of problems with the debug build.

All tests must of course pass with the final release build.

YMMV, of course. I have a habit of littering the code with debug-mode
asserts. As a result, the program runs 10 times slower in debug mode, but
often the reported bug triggers some assert much earlier in the code, and
then it becomes *much* easier to fix.

Cheers
Paavo

Paavo Helde

unread,
Oct 14, 2013, 1:12:07 PM10/14/13
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
news:l3h672$50q$1...@dont-email.me:

> On 14.10.2013 07:54, Paavo Helde wrote:
>> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
>> news:l3fif3$hq9$1...@dont-email.me:
>>>
>>> Any halfway decent C or C++ programmer knows about wrap-around for
>>> signed arithmetic.
>>
>> How can they "know" something which does not hold?
[...]
> It's unclear what your vague "does not hold" is intended to mean,

OK, I clarify. The following program aborts and dumps core on signed
overflow, clearly showing that there are current C++ implementations
where signed arithmetic does not wrap around, even on the x86 hardware.
Note that this is a fully standard-compliant behavior:

Test>cat test1.cpp
#include <time.h>

int main() {
int x = time();
return x+x;
}

Test>g++ test1.cpp -ftrapv -Wall

Test>./a.exe
Aborted (core dumped)

Test>g++ --version
g++ (GCC) 4.7.3
Copyright (C) 2012 Free Software Foundation, Inc.

Well, thank you for this long discussion. It got me thinking about these
problems, and as a result I decided to add -ftrapv to my common set of
gcc flags.

Cheers
Paavo

Alf P. Steinbach

unread,
Oct 14, 2013, 2:19:38 PM10/14/13
to
On 14.10.2013 19:12, Paavo Helde wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
> news:l3h672$50q$1...@dont-email.me:
>
>> On 14.10.2013 07:54, Paavo Helde wrote:
>>> "Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
>>> news:l3fif3$hq9$1...@dont-email.me:
>>>>
>>>> Any halfway decent C or C++ programmer knows about wrap-around for
>>>> signed arithmetic.
>>>
>>> How can they "know" something which does not hold?
> [...]
>> It's unclear what your vague "does not hold" is intended to mean,
>
> OK, I clarify. The following program aborts and dumps core on signed
> overflow,

It does not do that with default options for any compiler I know of.

So no, the code does not guarantee that, and it's not the default behavior.


> clearly showing that there are current C++ implementations
> where signed arithmetic does not wrap around,

No, it only shows that you can get trapping behavior.

Which has not been contested, and has been mentioned a number of times,
I think by me.

After all, we're discussing the formal UB that allows that.


> even on the x86 hardware.
> Note that this is a fully standard-compliant behavior:

Since we (or at least I) are discussing whether the standard should or
reasonably could be changed regarding allowing that behavior, i.e. since
that fact is the very basis of the discussion,

I gather that your admonition to "Note" that, is added just to HELP
readers who happen to read this out of context, rather than being
misdirection about your chosen opponent, for such readers.

Anyway, the example does not support your earlier assertion that
wrap-around behavior "does not hold", which was and is incorrect no
matter which reasonable interpretation of "does not hold" I try.

I.e., (1) your earlier "does not hold" assertion is still false.

To that incorrect assertion you have now added two further false
assertions, namely (2) the incorrect assertion that your program
generally "aborts and dumps core", which it only does with certain
special options with certain compilers, and which is nothing new in this
discussion, and (3) the incorrect assertion that the example
demonstrates a compiler that lacks wrap-around behavior, in a follow-up
to a posting demonstrating that that very same compiler does have
wrap-around behavior, and has that as default.

Summing up, an utter disregard for reason.


> Test>cat test1.cpp
> #include <time.h>
>
> int main() {
> int x = time();
> return x+x;
> }
>
> Test>g++ test1.cpp -ftrapv -Wall
>
> Test>./a.exe
> Aborted (core dumped)
>
> Test>g++ --version
> g++ (GCC) 4.7.3
> Copyright (C) 2012 Free Software Foundation, Inc.
>
> Well, thank you for this long discussion. It got me thinking about these
> problems, and as a result I decided to add -ftrapv to my common set of
> gcc flags.

That might be a good idea, or not.

A main problem is that the actual behavior then STILL is different from
gcc's optimization assumption of infinite precision arithmetic.

Which means you can still get broken assumptions, different release and
debug behavior, and so on, including now crashes in code that earlier
worked just fine... You do realize that you have to run through all test
suits again with your new flag, for all code that you intend to build
this new way? Both at the unit test level and higher.

Ian Collins

unread,
Oct 14, 2013, 3:18:32 PM10/14/13
to
David Brown wrote:
> On 14/10/13 11:54, Ian Collins wrote:
>> David Brown wrote:
>>> On 14/10/13 02:47, 嘱 Tiib wrote:
>>>
>>> Debugging effectively is always a bit of an art. Different types of
>>> program, different types of problem, and different types of compiler
>>> need different strategies. There are certainly times when you want to
>>> disable (or lower) optimisations to aid debugging.
>>>
>>> My point is that you do /not/ want to do your testing and debugging with
>>> different settings from your release code. (I know that "testing" and
>>> "debugging" are different things, but they are often done at least
>>> partly together.)
>>
>> Release testing maybe, but testing during development will often be done
>> with different settings and even (especially for embedded targets) on a
>> different platform.
>>
>
> Yes, as I noted earlier - at least during early development or for
> looking at specific problems. By the time you are testing the whole
> thing, you should be using the same settings as your final release will use.

I'll have to disagree with you there. There are many levels of testing
for most software products from developer unit tests through to formal
release testing.

Nearly all of the embedded products I have managed are developed using
host based tools and tested using simulated hardware in the same
environment. Only the final release testing is run with the target
hardware and build.

I have only encountered a handful of target only bugs using this process
and most of them were stack overflows. I'd guess that >99% of software
bugs are logic errors, so the platform and compiler settings are
irrelevant most of the time.

--
Ian Collins

Paavo Helde

unread,
Oct 14, 2013, 3:28:37 PM10/14/13
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote in
news:l3hck2$cf1$1...@dont-email.me:

>
> Since we (or at least I) are discussing whether the standard should or
> reasonably could be changed regarding allowing that behavior, i.e.
> since that fact is the very basis of the discussion,

Well, it seems you are alone discussing this topic. All other people have
been discussing the reality (the standard what we have now).

I agree that the standard could be changed to mandate wrap-around. Not
sure if it should, wrap-around in signed arithmetics seems a very silly
feature to me. Why should my huge bank account suddenly turn into a huge
debt? Or my burning hot oven jump below freezing? Have you an example
where the wrap-around would make sense (and which would not look like an
entry to obfuscated C contest)?

So far, it seems that the strongest point for putting the wrap-over into
the standard is that otherwise a bunch of x86 and other assembler
programmers might have some misconceptions about C++. Why should we care
so much about assembler programmers?

>> Well, thank you for this long discussion. It got me thinking about
>> these problems, and as a result I decided to add -ftrapv to my common
>> set of gcc flags.
>
> That might be a good idea, or not.
>
> A main problem is that the actual behavior then STILL is different
> from gcc's optimization assumption of infinite precision arithmetic.

No problem, I think gcc won't optimize in debug mode and will catch my UB
in the debug mode (i am regularly running the tests in debug mode, in
addition to the automatic release mode testing).

> Which means you can still get broken assumptions, different release
> and debug behavior, and so on, including now crashes in code that
> earlier worked just fine... You do realize that you have to run
> through all test suits again with your new flag, for all code that you
> intend to build this new way? Both at the unit test level and higher.

All tests are run after each commit, in the continous integration builds.
No problem here, other than that we don't yet have 100% code coverage in
tests. So I will probably switch off this flag in the stable branch
(actually delivered to customers) in order to avoid false positives at
customer site.

Cheers
Paavo

Dombo

unread,
Oct 14, 2013, 4:19:36 PM10/14/13
to
Op 14-Oct-13 18:57, Paavo Helde schreef:
> Öö Tiib <oot...@hot.ee> wrote in
> news:26715ece-ab3f-4cc6...@googlegroups.com:
>
>> Maintenance causes often new defects. Tester tests with
>> "release" build. Maintainer debugs findings of tester with "debug"
>> build. If the two behave differently then there may be additional
>> clarification needed. That possibly takes whole additional maintenance
>> cycle. Finally maintainer has to debug with "release" build to find
>> the cause of issue. That is additional inconvenience for anyway less
>> skilled developer.
>
> In my experience bugs which appear in release, but not in debug, are very
> rare.

That is my experience too. The vast majority of the bugs I have had to
deal last twenty years with exhibit themselves regardless of the
compiler settings. I've seen only a very few bugs (typically caused by
UB, rarely by a compiler bug) that only exhibit themselves with specific
compiler settings.

Debugging code compiled with aggressive optimizations can be very
confusing and even misleading, especially for less experienced
programmers. If the problem also reproduces with a debug build I see no
reason why one would go through the trouble of insisting on using a
release build for debugging.

I agree with others here that testing should always be done on release
builds.

Tobias Müller

unread,
Oct 14, 2013, 5:39:24 PM10/14/13
to
Paavo Helde <myfir...@osa.pri.ee> wrote:
> YMMV, of course. I have a habit of littering the code with debug-mode
> asserts. As a result, the program runs 10 times slower in debug mode, but
> often the reported bug triggers some assert much earlier in the code, and
> then it becomes *much* easier to fix.

Not to mention all the additional tests and assertions in the debug build
of the runtime library or inserted by the compiler, like:
- checked iterators
- heap corruption detection
- uninitialized variable detection
- leak detection

Of course that does not replace proper testing, but it certainly helps to
spot bugs much earlier and easier if turned on while developing.

Tobi
It is loading more messages.
0 new messages