This question is a result of another SO question.
Example Code
#include <iostream>
int main()
{
unsigned long b = 35000000;
int i = 100;
int j = 30000000;
unsigned long n = ( i * j ) / b; // #1
unsigned long m = ( 100 * 30000000 ) / b; // #2
std::cout << n << std::endl;
std::cout << m << std::endl;
}
Output
85
85
Compiling this code with g++ -std=c++11 -Wall -pedantic -O0 -Wextra
gives the following warning:
9:28: warning: integer overflow in expression [-Woverflow]
Questions
Am I correct in thinking that #1
and #2
invoke undefined behavior because the intermediate result 100 * 30000000
does not fit into an int
? Or is the output I am seeing well-defined?
Why do I only get a warning with #2
?
Yes, it is undefined behaviour, and the result you get is usually¹ different if unsigned long
is a 64-bit type.
¹ It's UB, so there are no guarantees.
Yes, this is undefined behaviour. What if you just stopped right there and return m
? The compiler needs to get from point A to point B, and you've told it to do it by making that calculation (which isn't possible). A compiler may choose to optimize this statement in such a way that you don't get an overflow, but as far as I know, the standard doesn't require the optimizer to do anything.
You're explicitly telling gcc not to optimize at all (-O0
), so my assumption is that it doesn't know the values of i
and j
at that point. Normally you'd learn the values because of constant folding, but like I said, you told it not to optimize.
If you re-run this and it still doesn't mention it, there's also the possibility that this warning is generated before the optimizer runs, so it's just not smart enough to do constant folding at all for this step.
1) Yes, it's undefined behavior.
2) Because #1 involves variables (not constants), so the compiler in general doesn't know whether it will overflow (although in this case it does, and I don't know why it doesn't warn).
You get a warning with two, because the compiler knows the values in the operand. The outputs are right because both use /b
which is unsigned long. The temporary value to be divisible by b
must be hold greater or equal datatype range, ( i * j )
or ( 100 * 30000000 )
are stored in a CPU register that has the same datatype range of the value to be divided, if b
was an int
the temporary result would be a int
, since b
is an ulong, int can't be divided by ulong, the temporary value is stored to an ulong.
It is undefined behavior if it overflows, but it's not overflowing in those cases
A program with the same structure, only changing b
to int
will have only two lines on .s code.
cltd
idivl (%ecx)
to b = int
movl $0,
%edx divl (%ecx)
to b = unsigned long,
idivl performs signed division, storing the value as signed
divl performs unsigned division, storing the value as unsigned
So you're right, the operation does overflows, the output is correct because of the division operation.
What is the difference of idivl and divl?
https://stackoverflow.com/a/12488534/1513286
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With