Good way to write integer overflow checks?

  • Thread starter Alf P. Steinbach
  • Start date
J

James Kanze

there is one mathematical overflow, it is not matter how machine
handle that [or standard handle that], yes i would know it handle that
overflwo using result "modulo 2^n"

It's a question of the abstraction used. The abstraction for
unsigned is modolo arithmetic, and in modulo arithmetic, there
is no overflow. It's not a question of "handling" overflow, but
of it simply not being possible.
i'm not much sure of the word "handle" that i mean "tratta"
it seems i lost my dictionary

"Handle" is about as good as anything else. (Etymologically,
"treat" would be closer, but I don't think that the difference
would be significant here.)
 
J

James Kanze

On 17/11/13 15:50, Sam wrote:
Unfortunately for you, although real-world processors are almost
invariably two's complement,

"Almost" is the operative word here. The most frequently
encountered processors are two's complement, but there are still
some exceptions.
C and C++ are very specifically /not/ two's compliment.
Signed overflow is explicitly given "undefined behaviour" in
the standards.

At least part of the motivation here was to allow an
implementation to do something intelligent, like generating a
trap. (Intel has, or at least had, special instructions to
support this efficiently.)
This means the compiler can generate code that assumes signed
overflow will never occur if that means better code.
In a case like "(a + b >= a)", with /signed/ types for "a" and "b", then
since the compiler /knows/ there can never be an overflow when the code
is run with valid inputs, it can simplify the expression to "b >= 0".

Good point. This sort of test is more or less along the lines
of "if (this == nullptr)" (which any decent compiler will
eliminate, since this can never be null).

In the end, of course, no competent programmer will knowingly
write code which overflows, so the issue is moot.
There was a thread about this recently. It was established that some
people /think/ compilers always treat signed integers as two's
compliment, and some people know that the standards say otherwise but
that it would be better if everything was defined as two's compliment
(especially in light of the incorrect assumptions made by the first lot
of people).

I would hope it was established that *not* all systems use two's
complement, and that on some systems, overflow can have some
really strange results (like making an "int" behave like a
floating point).
 
R

Rosario1903

Yes. In the past, it was a lot simpler. The compiler finished
one instruction before starting the next, and all reads and
writes were directly to memory---no caches and no pipelines.

in my way of see this, the order level of importance is:

1) the programmer has to understand what machine do
2) optimisation

yes i know i can make error on that but:

if for optimisation, programmer can not easily understand simple cpu
operations from high language level instruction: this is wrong

repeat with me: wrong!

after that, the cpu can, in its internal code, reorganize read write,
and mixing operations, ok for this;
but if there is one error there, in cpu reorganization of the code, it
is one bug of cpu, and programmer has not search that error
 
T

Tobias Müller

James Kanze said:
Good point. This sort of test is more or less along the lines
of "if (this == nullptr)" (which any decent compiler will
eliminate, since this can never be null).

In my experience most compilers actually don't.
I'm working with code that uses the following pattern quite often:

class Element
{
Element* GetChild(...) { return this != NULL ? GetChild_(...) : NULL; }
Element* GetChild_(...);
}

Element* root = ...;
// No NULL checks necessary
Element* element = root->GetChild(...)->GetChild(...)->GetChild(...);

I know it's UB but it seems to work with all compilers we used so far.

Tobi
 
G

Gerhard Fiedler

David said:
The code is portable. It is undefined behaviour on all platforms.
(With some compilers, and some flag options, it happens to give a
particular type of behaviour - but /any/ behaviour is consistent with
/undefined/ behaviour.) As it stands, the code asks for nasal daemons -
it might happen to give a check for signed overflow in some
circumstances, but that's just luck.


In the great majority of cases where people think they have found a
"COMPILER BUG", they are wrong. This is one of them. It is, IMHO, a
documentation "bug" - the gcc manual could be much clearer here.

The "-ftrapv" option does not stop signed overflow from being undefined
behaviour. This means that you are not guaranteed traps on overflows -
but you /might/ get them. It is dependent on many things, including the
types of optimisation, the target, the knowledge the compiler has (i.e.,
does it /know/ there is always an overflow, or never an overflow), etc.

FWIW (not much, but still), and no disagreement, my experience is that
-ftrapv works with the biggest size integer that is natively supported
(i.e. 32 bit ints on x86, 64 bit ints on x86-64).

Which sort of makes sense.

Gerhard
 
A

Alf P. Steinbach

It isn't an easy problem. One of the main offenders in this case
is <windows.h>. I've never found a reassurance in Microsoft
documentation that their min/max macros are not used in their
own functions. This means that using NOMINMAX in your code and
linking to something else that doesn't use it could potentially
lead to ODR violations.

Indeed.

Some years ago (I think less than a decade) I posted a min/max-fixed
version of Microsoft's <gdiplus.h>.

Checking, it still uses the min/max macros from <windows.h>, as of
Visual C++ 2013 (version 12.0):


<code>
#define NOMINMAX
#include <Windows.h>
#include <gdiplus.h>

auto main() -> int
{
MessageBox( 0, L"Hi", L"Info:", MB_SETFOREGROUND );
}
</code>


<compilation result>
1>c:\program files (x86)\windows
kits\8.1\include\um\gdiplustypes.h(500): error C3861: 'max': identifier
not found
1>c:\program files (x86)\windows kits\8.1\include
.... etc.
</compilation result>


<gdiplus.h> is mainly a header-only C++ module that wraps the separately
compiled GDI+ C API, i.e. a C++ language binding to that API, Microsoft
style. Happily most of the code is in a C++ namespace. So my old fix was
to add "using std::min" and "using std::max" in that namespace, as well
as a common include of <algorithm>, but this entailed fixing a lot of
sub-headers -- I don't recall exactly why now, but it was a lot of
editing, not a simple quick-fix.

(I think they do not use them; but that's not a certainty.)

See above. :(

Up until a few weeks ago, I've been avoiding the names "min" and
"max" in my own code, exactly to avoid problems. Then, I wrote a
little class intended to be a model of standard uniform random
number generator; and that *requires* those names.


To protect those names (including if you use
numeric_limits<>::min/max), you have two main alternatives:
either you parenthesize or you use a dummy macro like this:

#define NO_MACRO_EXPANSION
int x = std::numeric_limits< int >::max NO_MACRO_EXPANSION () ;

It prevents expanding any function-like macro named "max" for
the same reason that a ')' does: the macro name isn't followed
by an open parenthesis.

Well I think neither alternative is open for 3rd party code that one
includes.

So I think that the only reasonable way to do things is to define
NOMINAX and make sure to include <windows.h> oneself before including
any other headers.

That, by the way, is also necessary to avoid inconsistencies with
respect to supported OS version etc. (what declarations are included,
and even the form of the declarations).


Cheers,

- Alf
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top