- Unsigned fixed point multiply refresher - 4 Updates
- C++ exceptions are broken. - 2 Updates
"Öö 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:
Post a Comment