Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c++ > #67255
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Newsgroups | comp.lang.c++ |
| Subject | Re: In the end, rason will come |
| Date | 2019-08-02 20:46 +0200 |
| Organization | A noiseless patient Spider |
| Message-ID | <qi20dn$9g$1@dont-email.me> (permalink) |
| References | (1 earlier) <e04142c6-83a1-4b9d-835e-d41f5f138643@googlegroups.com> <qhs54u$gak$1@dont-email.me> <0caa44f2-e857-4121-8a0d-d54d4a0236bf@googlegroups.com> <qhvld8$d5a$1@dont-email.me> <8b28fb67-d7d6-4b17-a65b-7c020d5e5aa8@googlegroups.com> |
On 02/08/2019 04:53, Öö Tiib wrote:
> On Friday, 2 August 2019 00:26:16 UTC+3, David Brown wrote:
>> On 31/07/2019 23:22, Öö Tiib wrote:
>>> On Wednesday, 31 July 2019 16:30:21 UTC+3, David Brown wrote:
>>>> On 31/07/2019 12:48, Öö Tiib wrote:
>>>>>
>>>>> I agree that that signed arithmetic should not overflow
>>>>> in good code but it seems hard to find (or to teach) programmers
>>>>> who write good code in that respect. So there are often bugs with
>>>>> it and so I would often prefer crash (or have SIGFPE raised)
>>>>> instead of wrapping.
>>>>>
>>>>
>>>> I see a number of reasons why two's complement wrapping for signed
>>>> arithmetic is a bad idea.
>>>
>>> I was almost certain that you are one who is against it considering
>>> what you have written before.
>>>
>>>>
>>>> 1. It hinders optimisations. When you have division involved, you lose
>>>> several associative properties of integer arithmetic. And you lose all
>>>> sorts of basic identities that exist when you assume no overflow, such
>>>> as "x > y <=> (x + z) > (y + z)".
>>>
>>> That is true, (like I said), there are several optimizations that
>>> assume that integer arithmetic does not overflow and it will be
>>> tricky to indicate to compiler that it may do such optimizations
>>> in conforming mode.
>>
>> Such optimisations won't be tricky - they will be completely impossible.
>
> Why? Making compiler to warn that "(x + z) > (y + z)" is maybe sub-optimal
> code and to have some attribute or pragma for to suppress it when wrap
> *was* reason to write it like that is not impossible just tricky.
It is impossible, because the code would no longer be correct (unless
the compiler has other information about the ranges). If one of (x + z)
or (y + z) wraps and the other does not, optimising to "x > y" would not
be valid.
Try this with gcc:
bool foo(int x, int y, int z) {
return (x + z) > (y + z);
}
#pragma GCC optimize "-fwrapv"
bool foo2(int x, int y, int z) {
return (x + z) > (y + z);
}
(from the usual https://godbolt.org>
foo:
cmp edi, esi
setg al
ret
foo2:
add edi, edx
add edx, esi
cmp edi, edx
setg al
ret
When signed integer overflow has two's complement wrapping, you can't
simplify code using as many normal mathematical integer identities. You
can still do some re-arrangements and simplifications - more than if
overflow is defined as trapping - but you lose some case, especially
those involving relations and inequalities.
> Other likely outcome is that iterator or range based loops will simply
> be tiny bit more efficient than that int i as index.
> That can be even good. In Rust it is so and all are happy.
>
When the compiler has more information about the possible ranges of the
numbers involved, it can do more optimisation. That is the case for
iterators.
But it is not a good thing that some types of code become less efficient
- the fact that other types of code might not be affected does not
suddenly make it good.
A change that makes current, correct, working code slower is never a
good thing by itself. It is only worth having if there are significant
advantages. Turning broken code with arbitrary bad behaviour into
broken code with predictable bad behaviour is not particularly useful.
Let people who think that wrapping integers are somehow good or safe use
other languages. There is no need to weaken C++ with the same mistake.
>>> But so ... solution is same as with your #2.
>>
>>
>> Of course. Any defined behaviour on overflow restricts optimisations.
>> It's good to be able to choose what you want in different circumstances
>> - if you want traps on overflow to aid debugging, you don't mind the
>> cost in optimisations.
>>
>>>> 2. It makes alternative overflow behaviour wrong and non-conforming.
>>>> That means a compiler that has traps on overflows for safer coding or
>>>> during debugging, is suddenly no longer correct C++.
>>>
>>> The compiler vendors have never cared that with certain options
>>> their compiler is not conforming. Quite popular is to let to turn
>>> off exceptions, to turn off RTTI and so on. I do not think that turning
>>> off wrapping signed integers will be different in any way.
>>>
>>
>> It will be a completely different thing. And I think that is so obvious
>> it doesn't need more explanation.
>
> Ok. :/
>
>>
>>>> 3. It gives broken and incorrect code a defined behaviour, making it
>>>> harder for compilers and static analysis tools to inform the programmer
>>>> and help find mistakes. Instead of being able to tell you that you've
>>>> overflowed your integer constant (or constexpr) calculations, you'd have
>>>> to put the compiler in non-conforming modes first.
>>>
>>> Please elaborate. There are piles of warnings that a well-formed
>>> program with fully well-defined behavior will receive with -Wall
>>> and most warnings in -Wextra are such plus there are more warnings
>>> I like that neither of those settings turns on. No non-conforming
>>> modes are needed. So why should warning about potentially wrapping
>>> integer suddenly cause different situation?
>>>
>>
>> It is conceivable that compilers will have warnings for overflows on
>> signed arithmetic, even when it is fully defined - but I think it is
>> unlikely except with a view to compatibility for current standards.
>> Compilers don't warn when unsigned arithmetic overflows, because it is
>> fully defined.
>>
>> The difference is that when someone writes, for example, "if (a = 1)",
>> they have almost certainly made a mistake. So it is not unreasonable
>> for compilers to warn about it (and not unreasonable that it requires a
>> non-default flag to get the warning, since it is valid C or C++ code).
>> But if and when signed integer overflow becomes fully defined with
>> wrapping behaviour, it is no longer a mistake. How is the compiler
>> supposed to guess what was intentional behaviour and what was likely to
>> be a programmer error?
>
> I don't know who does not agree that signed integer overflow is likely
> a bug and worth warning about. If someone writes some code that uses
> deliberately wrapping (for example retroactively to test if overflow
> did happen) then they can suppress that warning locally with some
> pragma or attribute.
So you think this feature - wrapping overflows - is so useful and
important that it should be added to the language and forced upon all
compilers, and yet it also is so unlikely to be correct code that
compilers should warn about it whenever possible and require specific
settings to disable the warning? Isn't that a little inconsistent?
I'd be much happier to see some standardised pragmas like:
#pragma STDC_OVERFLOW_WRAP
#pragma STDC_OVERFLOW_TRAP
#pragma STDC_OVERFLOW_UNDEFINED
(or whatever variant is preferred)
where undefined behaviour is the standard, but people can choose
specific defined behaviour if they want, using a standardised method.
Basically, allow #pragma GCC optimize "-fwrapv" in a common form.
Surely that would be enough for those that want wrapping behaviour,
without bothering anyone else?
>
>
>>>> 4. Integer arithmetic that overflows is almost always in error - even if
>>>> the results are defined (by the language, compiler flags, etc.). If you
>>>> have a pile of 2147483647 apples and put another apple on the pile, a
>>>> result of -2147483648 is about the silliest answer you could possibly
>>>> give. Raising an error or saturating would be far more likely to be
>>>> correct.
>>>
>>> That option I would also love like I said above.
>>>
>>
>> That cannot be an option if wrapping is the defined behaviour.
>
> Note that both g++ and clang have -fwrapv and -ftrapv just neither
> of those work reliably.
>
"-fwrapv" does exactly what it says on the tin, as far as I know. Do
you know of any problems with it, or any way in which it does not work
reliably? Neither gcc nor clang are bug-free, of course, and given that
this is an option that is used rarely it will not receive as much heavy
testing as other aspects of the tools. But the intention is that this
is a working and maintained option that gives you specific new semantics
in the compiler.
"-ftrapv" has always been a bit limited, and somewhat poorly defined.
It is not clear how much rearrangement is done before the trapping
operations are used, and support varies from target to target. AFAIUI,
you are recommended to use -fsanitize=signed-integer-overflow instead.
> Requiring at least working -fwrapv would be bit better
> than nothing.
Again, what do you think does not work with -fwrapv?
> I could bit cheaper (or at least bit more readably) write
> my own throwing or saturating integers using wrapping integers.
For gcc and clang, you are better off using the overflow arithmetic
builtin functions.
> I like trapping best but I understand that it is most expensive on
> majority of platforms.
>
It is expensive on /all/ platforms, because it disables a good many
re-arrangements and simplifications of expressions. Beyond that, it
usually boils down to adding "trap if overflow" or "branch if overflow"
instructions after arithmetic operations - and the cost of that does
vary between targets.
But it is probably cheaper than saturating arithmetic in many cases,
which also disables many re-arrangements and requires extra code for
targets that don't have saturating arithmetic instructions.
>> It is fine to have flags that add semantics to the language in place of
>> undefined behaviour. It is not fine to have flags that give completely
>> different semantics to something the language defines. This is not like
>> enabling or disabling particular features, such as exceptions or RTTI
>> (features which are typically only disabled for niche uses, such as
>> resource limited embedded systems).
>
> Yes, you have good point here that switching between different well
> defined behaviors is evil.
> It is maybe niche ... but it is niche that we *own* right now.
> I have about third of my career (plus sometimes as hobby) participated
> in such projects. There are literally tons of electronics made all
> around and other languages but C and C++ are only sometimes
> experimentally tried there. Also the optimizations often matter
> only on such limited systems somewhat.
>
My career has been dominated by programming on platforms which are small
enough that you typically disable exceptions and RTTI when using C++.
(Things might change in the future with the newer ideas for cheaper C++
exceptions.) And yes, it is a very important niche, especially for C.
>
>>>> 5. It gives the impression that signed integer arithmetic is now "safe"
>>>> and can be carried out without as much care and attention as you had to
>>>> do when there were nasal demons lurking round the bend. This is, of
>>>> course, nonsense - it merely hides some classes of errors under the
>>>> carpet where they are harder to find.
>>>
>>> It is frequently same with unsigned integers. On lot of cases when
>>> these overflow then it is defect.
>>>
>>
>> Agreed. I would be happy for unsigned arithmetic to be undefined on
>> overflow in most cases. However, there are occasional situations where
>> wrapping behaviour is clearly useful and correct, so it is important
>> that there is /some/ way to get it. If unsigned arithmetic were not
>> defined with wrapping in C and C++, there would have to be another way
>> to do it for these occasional uses.
>
> Yes, some of the code to avoid that undefined behavior with signed
> integers uses unsigned right now (and assumes twos complement).
Indeed - and that is often fine, though perhaps of limited portability.
(Making two's complement representation a requirement will fix this.)
> The well-defined behavior can not make wrong code to give
> correct answers. The wrong answers will just come more consistently
> and portably with wrap and so I can trust unit tests for my
> math of embedded system ran on PC bit more.
>
If your unit tests rely on wrapping for overflow, then those unit tests
are broken. "More consistent wrong answers" is /not/ a phrase you want
to hear regarding test code! You want your embedded code to run quickly
and efficiently - but there is no reason not to have
-fsanitize=signed-integer-overflow for your PC-based unit tests and
simulations.
>>>> So I am against defining the results of signed integer arithmetic
>>>> primarily for code safety and correctness reasons, and secondarily for
>>>> performance reasons.
>>>
>>> But you put performance reason as #1 in above list. ;)
>>>
>>
>> It was not ordered by importance (or what I feel is most important).
>> Correctness always trumps efficiency, and aids to correctness are
>> therefore high on my list of important features.
>
> I have same views especially about arithmetic that is fast. For
> massive amounts of algebra it is better to use libraries that
> utilize GPU or the like anyway. I still do not understand how
> undefined behavior is supposed to be more safe and reliable than
> defined behavior ... but perhaps it is just me.
>
Undefined behaviour is something that your tools know is wrong. That
means that there is at least a chance that the tools can spot mistakes.
They can't do it all the time, and sometimes there are significant costs
in run-time tests for find mistakes, but it is possible. Look at the
sanitize options at
<https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html> (or
the clang page if you prefer), to see some of the mistakes that can be
caught at compile time. They can only be caught because they are
mistakes - they have no defined behaviour.
If something is given defined behaviour by the language, then the
compiler must assume that when it occurs, it is intentional. At best,
any compile-time or run-time checking for this must be an optional
feature with a high risk of false positives.
Additionally, adding this new semantics to the language would likely
confuse people and make them think it was always the case. There are
far too many programmers already who think signed arithmetic wraps in C
and C++ - it would be worse if they see it documented for some standards
and not others.
If there were enough benefit from the additional behaviour, that would
be fair enough. But there isn't any benefit of significance - correct
code remains correct after this change, and broken code remains broken.
Back to comp.lang.c++ | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll thread
In the end, rason will come Bonita Montero <Bonita.Montero@gmail.com> - 2019-07-31 10:14 +0200
Re: In the end, rason will come "Chris M. Thomasson" <invalid_chris_thomasson_invalid@invalid.com> - 2019-07-31 01:25 -0700
Re: In the end, rason will come James Kuyper <jameskuyper@alumni.caltech.edu> - 2019-07-31 07:58 -0400
Re: In the end, rason will come Siri Cruise <chine.bleu@yahoo.com> - 2019-07-31 05:14 -0700
Re: In the end, rason will come Jorgen Grahn <grahn+nntp@snipabacken.se> - 2019-07-31 22:35 +0000
Re: In the end, rason will come "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-07-31 14:59 +0200
Re: In the end, rason will come Öö Tiib <ootiib@hot.ee> - 2019-07-31 03:48 -0700
Re: In the end, rason will come David Brown <david.brown@hesbynett.no> - 2019-07-31 15:29 +0200
Re: In the end, rason will come Öö Tiib <ootiib@hot.ee> - 2019-07-31 14:22 -0700
Re: In the end, rason will come David Brown <david.brown@hesbynett.no> - 2019-08-01 23:25 +0200
Re: In the end, rason will come Öö Tiib <ootiib@hot.ee> - 2019-08-01 19:53 -0700
Re: In the end, rason will come David Brown <david.brown@hesbynett.no> - 2019-08-02 20:46 +0200
Re: In the end, rason will come Öö Tiib <ootiib@hot.ee> - 2019-08-05 02:56 -0700
Re: In the end, rason will come David Brown <david.brown@hesbynett.no> - 2019-08-06 14:32 +0200
Re: In the end, rason will come Manfred <noname@add.invalid> - 2019-08-07 16:05 +0200
Re: In the end, rason will come David Brown <david.brown@hesbynett.no> - 2019-08-07 17:00 +0200
Re: In the end, rason will come "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-08-07 18:39 +0200
Re: In the end, rason will come David Brown <david.brown@hesbynett.no> - 2019-08-07 21:15 +0200
Re: In the end, rason will come David Brown <david.brown@hesbynett.no> - 2019-08-07 21:20 +0200
Re: In the end, rason will come Ralf Goertz <me@myprovider.invalid> - 2019-08-08 12:14 +0200
Re: In the end, rason will come Ralf Goertz <me@myprovider.invalid> - 2019-08-08 12:21 +0200
Re: In the end, rason will come Christian Gollwitzer <auriocus@gmx.de> - 2019-08-02 07:41 +0200
Re: In the end, rason will come Keith Thompson <kst-u@mib.org> - 2019-07-31 13:05 -0700
Re: In the end, rason will come Öö Tiib <ootiib@hot.ee> - 2019-07-31 15:48 -0700
Re: In the end, rason will come Keith Thompson <kst-u@mib.org> - 2019-07-31 16:32 -0700
Re: In the end, rason will come Öö Tiib <ootiib@hot.ee> - 2019-07-31 19:21 -0700
Re: In the end, rason will come Bonita Montero <Bonita.Montero@gmail.com> - 2019-08-02 08:10 +0200
Re: In the end, rason will come David Brown <david.brown@hesbynett.no> - 2019-08-06 14:54 +0200
csiph-web