I am learning C++ by reading Stroustrup\'s \"Principles and Practice Using C++\".
In the section about pre- and post-conditions there is the following example of functio
No, there aren't any values that will, within the bounds of defined behavior of standard C++, violate the post-condition. However, there are values that still can make the function behave incorrectly, namely values so large that their product does not fit within an integer. Try passing 200'000 and 15'000.
Due to the way most compilers implement C++, you might see the post-condition being violated, but what you're actually observing is undefined behavior due to integer overflow.
So basically, positive values in multiplication ... result in Positive values but these may not actually fit the result type .
Your precondition is not complete, and you postcondition is also invalid. Not only you can get negative values but also positive values that are just smaller than the input value, all you need is sufficiently large values as input such that the wrap around goes beyond zero, i.e. a long-wrap-around .
You can use this :
bool multiplication_is_safe(uint32_t a, uint32_t b) {
size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
return (a_bits+b_bits<=32);
}
to guard against overflow, but then you would want to employ additional checks for FALSE-Positives .
Alternatively if performance is not that much of an issue you can use MPZ library. If performance is an issue and you want to write assembly for a CPU that has an overflow flag, then you can do just that. It is possible that your compiler also can do the checks for you e.g. G++ has fno-strict-overflow
or maybe cast to unsigned int
after the precondition check.
At any rate, most of these solutions do not actually solve your problem that results will be foo, that is that you might get smaller area than the actual result.
So your only safe choice is to allow only safe multiplications as shown herein, doing that you miss something, but not that much.
Multiplication of values that overflow the bit representation of value type is undefined because the number of bits overflowed could be more than 1. Thus you could end up with a positive or negative sign bit and the number of lost bits is variable.
Example 1: INT_MAX * 2
: result is correct but since the high bit represents the sign bit it is not corrected represented for its type.
Example 2: INT_MAX * 4
: 1 bit is lost to overflow and the the sign bit is incorrect like in the previous example.
Example 3: (INT_MAX + 1) * 2 = 0
: due to overflow of all set bits but sign is correct.
I am using a 8 bit binary representation to make it easier to read, to show why this happens.
0111 1111 // Max positive signed value
+1
1000 0000 // Sign bit set but binary value is correct
*2
0000 0000 // Upper bit is lost due to overflow
In this case there is both soft overflow, no lost information but the representation is incorrect. And hard overflow where the bit is no longer present in the result.
The difference in the overflows is how the overflow can be detected. Typically hard overflows will be detected by the hardware and require very little work for the software to handle. However software overflows may require the software to explicitly test for the overflow condition because the hardware typically does not recognize a sign bit in integer math operations.
How the run-time library handles the overflow is up to the library. Most will ignore it because it is faster to do so, while others may throw an error. Undefined behavior does not mean it might format your disk. The result of a math operation does not alter the flow of code in any way except as the logic of the code dictates. It can ignore the overflow or try to handle it in some way. The standard does not dictate what method to employ if the code or the hardware tries to handle the problem.
Basically three there are 3 possible things that can happen.
1. The overflow is ignore and the returned value in invalid.
2. The overflow is ignored by the run-time library but the hardware throws an error that is also ignored, resulting in a hard failure of the running code. In this situation it is completely up to the OS to determine what happens next. Going nuts and destroying data would to a poor design decision.
3. The overflow is handled by the run-time library which must determine the best way to proceed. Typically this means giving the code a chance to catch the error and handle it, or by shutting down the code as graceful as possible.