In fact, prior to Java 8, you could not even declare unsigned integers.
In Java 8 you still can't declare unsigned integers. It just that API have been
extended to provide operations that treat an int or a long as having unsigned
value.
This seems to imply that Java leaves the behaviour of signed integer overflow
up to the underlying hardware, but guarantees a two's complement
representation even if the architecture does not. It seems reasonable to
expect that it would simply wrap to 0 (I wasn't able to find a more
conclusive reference for this).
In fact Java Specification does specify what happens in case of overflow very
precisely. For example for multiplication (section 15.17.1):
If an integer multiplication overflows, then the result is the low-order bits
of the mathematical product as represented in some sufficiently large
two's-complement format.
And for addition (section 15.18.2):
If an integer addition overflows, then the result is the low-order bits of the
mathematical sum as represented in some sufficiently large two's-complement
format.
Java approach without undefined behaviour would seem to be quite convenient for
a programmer, but it does not seem to be the case in practice. Of course you
gain ability to check for overflow after performing the operation, which is
nice, but at the same time loose the straightforward way to detect bugs
provided by undefined behaviour (UB).
If an application executes UB, then it is obviously wrong, thus a simple
instrumentation of arithmetic operation followed by a check for overflow gives
a way to detect such problems without false positives (see for example
-fsanitize=undefined and similar options available in modern C/C++ compilers).
IMHO only very small fraction of overflow bugs would have been fixed by just
wrapping result around.
Of course you gain ability to check for overflow after performing the operation,
Using exceptions?
IMHO only very small fraction of overflow bugs would have been fixed by just wrapping result around.
Yes, switching to wrapping integers is not the solution to overflow. Rather check the operation beforehand. I still think using unsigned integers is always preferred if you don't need negative values.
What I had in mind, is the fact that with defined overflow you could
unconditionally perform the operation, and then observe the result to decide if
overflow have in fact happened. For example, following pattern to check if
adding 100 to an integer 'a' overflows would be valid:
int a = ...;
if (a + 100 < a) overflow
So this is one of cases where wrapping behaviour could potentially fix bugs in
existing applications. Obviously, rewriting this to work with overflow is
trivial if you are already aware of undefined behaviour that could occur.
Suppose somebody have written above code. Then under current C++ standard it
would invoke UB on overflow. But, if standard were to change and require
wrapping behaviour for int, then it would be "fixed", i.e, do what programmer
intended it to do. Similarly if you use some compiler specific option to ensure
wrapping behaviour for signed integers like GCC -fwrapv.
If an application executes UB, then it is obviously wrong, thus a simple instrumentation of arithmetic operation followed by a check for overflow gives a way to detect such problems without false positives (see for example -fsanitize=undefined and similar options available in modern C/C++ compilers).
IMHO only very small fraction of overflow bugs would have been fixed by just wrapping result around.