Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it undefined behavior if the intermediate result of an expression overflows?

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

  1. 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?

  2. Why do I only get a warning with #2?

like image 460
Jesse Good Avatar asked Sep 19 '12 04:09

Jesse Good


4 Answers

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.

like image 167
Daniel Fischer Avatar answered Sep 28 '22 20:09

Daniel Fischer


Intermediate result

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.

Why no error when they're variables?

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.

like image 35
Brendan Long Avatar answered Sep 28 '22 20:09

Brendan Long


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).

like image 39
user1610015 Avatar answered Sep 28 '22 20:09

user1610015


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

like image 23
snoopy Avatar answered Sep 28 '22 19:09

snoopy