Sunday, March 13, 2016

Digest for comp.lang.c++@googlegroups.com - 6 updates in 2 topics

"Öö Tiib" <ootiib@hot.ee>: Mar 12 04:09PM -0800

On Sunday, 13 March 2016 01:06:55 UTC+2, David Brown wrote:
> use two's complement. However, C (and C++) explicitly makes signed
> arithmetical overflow undefined behaviour, even when you are using types
> that must be two's complement.
 
On case of C++ the traits in 'numeric_limits' can indicate if it is
undefined behavior or not.
 
> integer overflow to avoid undefined behaviour, even though the
> instructions used by the compiler will likely be two's complement
> arithmetic instructions that will probably give you the answer you expect.
 
Yes that is true but it is controversial topic. Lot of C and C++
programmers would be actually somewhat happier with implementation
defined behavior instead of undefined behavior on case of arithmetic
overflow of signed value.
 
Even implementation-defined behavior is nuisance of course. For example
sign extending on case when counts of bits to extend are not known
compile time. On most platforms that works:
 
int const bits_to_extend = 64 - bits_in_source;
int64_t result = ((int64_t)source << bits_to_extend) >> bits_to_extend;
 
Right shift of a negative signed number has implementation-defined
behavior by standard however. So it only works because most platforms
have right shift that extends sign. What really should be done in
portable code is something like that I suspect:
 
int64_t const mask = 1ULL << (bits_in_source - 1);
int64_t result = (source ^ mask) - mask;
 
If bits above 'bits_in_source' in 'source' may be are not zero
then additionally clearing those is needed:
 
int64_t tmp = (int64_t)source & ((1ULL << bits_in_source) - 1);
int64_t const mask = 1ULL << (bits_in_source - 1);
int64_t result = (tmp ^ mask) - mask;
 
Everybody likely agrees that it looks like cryptic garbage. I am not
100% sure that it takes care of all issues.
David Brown <david.brown@hesbynett.no>: Mar 13 09:55PM +0100

On 13/03/16 00:38, Alf P. Steinbach wrote:
 
> Not quite. C++ only makes overflow UB for types where "the result is not
> mathematically defined or not in the range of representable values for
> its type".
 
True.
 
 
> This does not apply to unsigned types because there the result is always
> in range.
 
True.
 
 
> And more generally it does not apply to an integer type T where
> numeric_limits<T>::is_modulo is true.
 
OK.
 
 
> With MinGW g++ 5.1.0 (a version of g++ for Windows), all signed integer
> types have is_modulo false even with use of the flag `-fwrapv`, or at
> least my hasty but not entirely complete testing right now says so.
 
An implementation is free to give specific behaviour to things that are
undefined in the standards. That does not change the standard. The
standard does not make any requirements about the overflow behaviour of
signed ints - the behaviour is undefined in standard C and C++. That is
the case even if it is defined in some particular implementations of the
language, such as MSVC or gcc with -fwrapv.
 
(It would surprise me a little if MSVC implements wrapping behaviour on
all signed integers, since that blocks certain types of optimisation.
But it would also surprise me if it sets is_modulo true but does not
follow those requirements.)
 
>> arithmetic instructions that will probably give you the answer you
>> expect.
 
> I agree, when the code must be portable between compilers.
 
Yes. If your compiler (or the flags you use) document specific
behaviour for signed integer overflow, and your code is only targeted at
such compilers, then of course it is fine to rely on that behaviour.
I'd add a comment in the code, however!
David Brown <david.brown@hesbynett.no>: Mar 13 10:50PM +0100

On 13/03/16 01:09, 嘱 Tiib wrote:
> programmers would be actually somewhat happier with implementation
> defined behavior instead of undefined behavior on case of arithmetic
> overflow of signed value.
 
It is always possible for an implementation to define the behaviour when
the standards say "undefined behaviour". So it is a choice made by the
compiler developers to make integer overflow undefined, rather than
giving it a specific implementation-defined behaviour. The fact that
most compilers don't have implementation-defined signed overflow
behaviour by default suggests that programmers who want that are in the
minority - but there are enough to make it worth supporting the -fwrapv
flag in gcc and clang that /does/ define signed overflow behaviour.
 
Note that there is a large difference between the standards calling the
behaviour "undefined", and calling it "implementation-defined". If it
is implementation-defined, the compiler developer is free to make a
choice of how they handle overflow (wrapped/modulo behaviour,
saturation, trapping, etc.). But when they have made that choice and
documented it, they must stick to it. With undefined behaviour, the
compiler can use different methods in different circumstances - whatever
makes the code simplest and fastest when there is no overflow (which of
course is the normal behaviour).
 
What I think would be a better compromise that avoids worries of nasal
daemons while still letting the compiler optimise freely would be to
make signed overflow "unspecified behaviour" (divide by zero can still
be undefined). In other words, multiplying two valid "int" values will
always give you an "int" - but if the compiler can't give you the
correct int, it can give you any int if finds convenient. That might be
the result from two's complement rounding, but it could be 0 or it could
be whatever happens to be in a register at the time.
 
> int64_t result = (tmp ^ mask) - mask;
 
> Everybody likely agrees that it looks like cryptic garbage. I am not
> 100% sure that it takes care of all issues.
 
The thing about implementation-defined behaviour is that you need to
check that it works on the compilers you use - but once you have checked
(in the documentation, not trial-and-error compilation!), you can rely
on it. If you know your code is targeted at gcc, clang, MSVC, icc, and
perhaps a few other major compilers, all of which describe how they
implement negative integer shifts, then you can be happy with code like
your first version.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Mar 13 11:46PM +0100

On 13.03.2016 22:50, David Brown wrote:
> correct int, it can give you any int if finds convenient. That might be
> the result from two's complement rounding, but it could be 0 or it could
> be whatever happens to be in a register at the time.
 
Well, the main attraction of UB is that the compiler can assume that UB
does not occur, and optimize accordingly.
 
One main attraction of well-defined behavior is, as you write, the
absence of possible nasal daemons. And if that were the only advantage
then some unspecified behavior could be a useful compromise (I'm not
entirely clear on this, I worry about lost optimizations). However,
another main attraction, the one that trumps all else in my humble
opinion, is PREDICTABILITY: that given some source code, its effect for
given inputs can be ~100% reliably predicted by the programmer.
 
That means it can be reasoned about. :)
 
[1]Java's quite acceptable numeric performance – in some cases even
outperforming C++ (I think due to JIT compilation which can optimize for
the environment at hand) – is evidence that well defined wrapping
behavior for signed integer arithmetic needs not incur significant cost.
 
Partially due to that evidence I regard the technically possible
optimizations as mostly premature micro- and nano-optimizations, doing
more harm than good – like a beginner "optimizing" loops with goto's
maybe can shave a nanosecond or two, but pays unreasonably high cost.
 
 
Cheers!,
 
- Alf
 
Notes:
[1] <url:
http://stackoverflow.com/questions/3001836/how-does-java-handle-integer-underflows-and-overflows-and-how-would-you-check-fo>
Hergen Lehmann <hlehmann.expires.5-11@snafu.de>: Mar 12 04:50PM -0600

Am 12.03.2016 um 21:46 schrieb Mr Flibble:
 
> because you can often recover from a std::runtime_error but a
> std::logic_error exception is often a sign that something is seriously
> screwed and the only safe course of action is to terminate the
process.
 
I don't see any basis for that assumption.
 
std::logic_error includes stuff like std::domain_error (which is not
even used by the STL) or std::invalid_argument, which might be some
completely harmless rejection of input data.
On the other hand, std::runtime_error includes std::system_error, which
might be quite hazardous depending on which system operation did fail.
 
Hergen
 
 
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
"James K. Lowden" <jklowden@speakeasy.net>: Mar 13 03:55PM -0600

On Sat, 12 Mar 2016 14:46:06 CST
> std::logic_error a std::string object is created for the exception
> message which can cause std::bad_alloc (a std::runtime_error
> exception) to be thrown replacing the originally intended exception.
 
Oh, so you mean that the programmer may do something in handling the
exception to convert logic_error to runtime_error. Sure.
 
The programmer also has control over that. If it's important to pass
logic_error up the stack unmolested, do not allocate any objects in the
handler, or wrap that logic in its own try block. I've heard that in
some shops it's standard practice to use only static strings in
handlers to avoid the situation you describe.
 
>From my point of view, if std::string triggers bad_alloc, you're hip
deep in alligators because your heap is corrupted. Likely is the
logic_error is itself spurious, a knock-on effect of your pointers
spending their spring break in Fort Lauderdale. So, yeah, grap
__FILE__ and __LINE__, and head for the exit as soon as possible.
 
--jkl
 
 
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: