Community
cancel
Showing results for
Did you mean:
Beginner
273 Views

Same operation, different results

Hi,

When executing the program below, icc produced different results for Y and Y2. The result of Y is 12. I guess it multiplied the VALUE constant first, and got the result of 4, then cast the int 4 as long long type. Finally it is multiplied by 3 to get 12. And the value of Y2 is -4611686044197191668. Instead, it is possible to convert the type t to long long first and then multiply the variables t to produce the result of 4611686009837453316 which is long long type. Finally it is multiplied by 3 to get -4611686044197191668.

Is this a correct behavior for intel c complier?  Sorry if i misunderstood something.

TestCase:

#include <stdio.h>

#define VALUE ((int)((long long)U1 * (long long)3) + 2)

int main(void)

{

long long Y, Y2;

int U1;

int t;

U1 = -2147483648;

Y = ((long long)(VALUE * VALUE) * 3);

t = VALUE;

Y2 = ((long long)(t * t) * 3);

if (Y != Y2)

printf("%lld,%lld\n", Y, Y2);

return 0;

}

The OS is:

Linux version 4.15.0-65-generic

Compiler Version:

icc (ICC) 19.0.4.243 20190416

Output:

12,-4611686044197191668

Expected output:

12,12

17 Replies
Black Belt
273 Views

t is an int (t*t) produces an int (and in this case an overflowed value)

(long long)t produces a long long representation of t

((long long)t)*t would produce the correct (and desired) result

You need to place your type casting properly (according to the established rules of operation for C/C++).

Note, you are also down-casting the result, thus truncating the 32 msb's

Jim Dempsey

Beginner
273 Views

jimdempseyatthecove (Blackbelt) wrote:

t is an int (t*t) produces an int (and in this case an overflowed value)

(long long)t produces a long long representation of t

((long long)t)*t would produce the correct (and desired) result

You need to place your type casting properly (according to the established rules of operation for C/C++).

Note, you are also down-casting the result, thus truncating the 32 msb's

Jim Dempsey

Hi Jim

When the type casting is ((long long)t) * t, the result of Y2 is -4611686044197191668. But why is it also -4611686044197191668 when the type casting is (long long )( t * t), shouldn't it be 12?

Regards

Xing

Black Belt
273 Views

IMHO (t*t) should produced an intermediary result of the type of t

-2147483648 = 0x80000000

The 32-bit result of 0x80000000 * 0x80000000 = 0x00000000  i.e. (t*t)
0 * 3 = 0, thus Y2 should have been 0, not 12

When promoting one or both of the numbers to 64 bits

0xFFFFFFFF80000000 * 0xFFFFFFFF80000000 = 0x4000000000000000

0x4000000000000000 * 3 = 0xC000000000000000 - -4611686018427387904

What you observe here may be the result of ambiguity in the specification, thus leaving this to an implementation issue. I am not savvy enough with the standards to provide a definitive answer. You can affect the desired result with the insertion of additional casting:

(long long)(int(t*t))

assuming that was your desired result.

Jim Dempsey

Employee
273 Views

This is UB, According to C++11 standard, Chapter 5, note 4

If during the evaluation of an expression, the result is not mathematically defined or not in the range of
representable values for its type, the behavior is undefined.

if you compile with O0 you get expected 12,12 since it uses real registers. BTW GCC up to 4.7 have different output (-4611686044197191668,12)

But was is a rationale with playing with integer overflow?

Beginner
273 Views

jimdempseyatthecove (Blackbelt) wrote:

IMHO (t*t) should produced an intermediary result of the type of t

-2147483648 = 0x80000000

The 32-bit result of 0x80000000 * 0x80000000 = 0x00000000  i.e. (t*t)
0 * 3 = 0, thus Y2 should have been 0, not 12

When promoting one or both of the numbers to 64 bits

0xFFFFFFFF80000000 * 0xFFFFFFFF80000000 = 0x4000000000000000

0x4000000000000000 * 3 = 0xC000000000000000 - -4611686018427387904

What you observe here may be the result of ambiguity in the specification, thus leaving this to an implementation issue. I am not savvy enough with the standards to provide a definitive answer. You can affect the desired result with the insertion of additional casting:

(long long)(int(t*t))

assuming that was your desired result.

Jim Dempsey

Sorry for the previous ambigious statement, t is -2147483646, not -2147483648.

-2147483646 = 0x80000002

If the expression is ((long long)(t * t) * 3), it will first play with integer overflow because (t*t) produce an int and then cast int to long long.

0x80000002 * 0x80000002 = 0x3FFFFFFE00000004(integer overflow)

0x00000004 * 3 = 0x0000000C

So, the result is 12.

If the expression is (((long long)t) * t * 3), it will first cast int to long long and then multiply 3.

0x0000000080000002 * 0x80000002 =0x3FFFFFFE00000004

0x3FFFFFFE00000004 * 3 = 0xBFFFFFFA0000000C

So, the result is -4611686044197191668, which is 0xBFFFFFFA0000000C’s decimal.

In the above program, T is equal to VALUE and it does the same operation, which is ((long long)(x * x) * 3). According to the rules, they should produce the same result 12, but ((long long)(VALUE * VALUE) * 3) = 12, and ((long long)(t * t) * 3) = -4611686044197191668. I wonder why they produce different results ?

Regards

Xing.

Beginner
273 Views

This is UB, According to C++11 standard, Chapter 5, note 4

If during the evaluation of an expression, the result is not mathematically defined or not in the range of
representable values for its type, the behavior is undefined.

if you compile with O0 you get expected 12,12 since it uses real registers. BTW GCC up to 4.7 have different output (-4611686044197191668,12)

But was is a rationale with playing with integer overflow?

So is this an optimization problem for the icc compiler?

Regards

Xing

Black Belt
273 Views

>>...since it uses real registers
The operations always use real registers.

IMHO, the (t*t) should have been computed using 32-bit registers, then promoted to 64-bit register.

This said, I suggest using an unambiguous cast:  (long long)(int(t*t))

Jim Dempsey

Employee
273 Views

jimdempseyatthecove (Blackbelt) wrote:

>>...since it uses real registers
The operations always use real registers.

Of course this is my wrong statement:) I meant in O0 mode everything is computed in runtime using 32 bit registers without optimizations, in optimized mode the result is precompiled and loaded directly to result register without re-computation

Employee
273 Views

qu, xing wrote:

So is this an optimization problem for the icc compiler?

Regards

Xing

No, everything is according to C++ standard: garbage in -> garbage out. Standard says if you have an integer overflow an execution/compiler behavior is undefined.

Black Belt
273 Views

The problem isn't just integer overflow. In this particular case, the code (as was written) is relying on the integer overflow to be properly masked off at 32-bits.

qu, xing parenthesized (t*t) which should have produced a 32-bit intermediary result (granted with overflow, which should have been discarded). The compiler should not have performed the promotion to 64-bits until after the parenthetical operation had completed.

Consider for the moment the consequence of;

(long long)(t << 8) // rely on bits shifted out of 32-bit value and into the bit bucket (no different from overflows to be delegated to bit bucket)

Where initial value of t is 0xFF000001

The result should be 0x0000000000000100

Jim Dempsey

Employee
273 Views

Well, first of all a programmer should follow the C++ standard. If the standard says UB then both gcc and icc are following the standard in their (gcc in more expected and icc in less expected) way  :-)

Try this version which promotes "t" first and removes UB:

```#include <stdio.h>

#define VALUE (0x7FFFFFF & (U1 * 3ll) + 2)

int main(void)
{
long long Y, Y2;
int U1;
int t;
U1 = -2147483648;
Y = ((long long)(VALUE * VALUE) * 3);
t = VALUE;
Y2 = (0x7FFFFFF & ((long long)(t)*(t))) * 3;
if (Y != Y2)
printf("%lld,%lld\n", Y, Y2);
return 0;
}```

Black Belt
273 Views

The point is not how to remove the UB.

Rather the point is the compiler is not generating code that produces/uses the precision of the parenthesized expression. This is in error.

Your line 13 is not the correct form of the original post expression. you have:

Y2 = (0x7FFFFFF & ((long long)(t)*(t))) * 3;
not     Y2 = (0x7FFFFFF & ((long long)((t)*(t)))) * 3;
or       Y2 = (0x7FFFFFF & ((long long)(t*t))) * 3;

In your case, the position of the up-cast was ambiguous, and thus permitted to be implementation defined
In the OP's case, ((long long)(t*t), there is no ambiguity as to how to perform the statement.

Jim Dempsey

Employee
273 Views

jimdempseyatthecove (Blackbelt) wrote:

Your line 13 is not the correct form of the original post expression. you have:

So you think if the answer for _int64(t*t) will be "3" instead of "4" it will be a correct UB result?

jimdempseyatthecove (Blackbelt) wrote:

Your line 13 is not the correct form of the original post expression. you have:

No, it is not. It is corrected from the starter post to remove standard's UB.

Beginner
273 Views

It is worth mentioning that ((long long)(t * t) * 3) will be "12" when there is no VALUE.

Testcase:

#include <stdio.h>

int main(void)

{

long long Y2;

int t;

t = -2147483646;

Y2 = ((long long)(t * t)) * 3;

printf("%lld\n",Y2);

return 0;

}

The OS is:

Linux version 4.15.0-65-generic

Compiler Version:

icc (ICC) 19.0.4.243 20190416

Output:

12

Regards

Xing

Beginner
273 Views

Now I know that the expression Y2 = ((long long)(t * t) * 3) will play with overflow and produce an undefined behavior according to C11. And the expression Y2 = (0x7FFFFFF & ((long long)(t)*(t))) * 3 will remove the UB.

But the point I really want to make is that Y2 = ((long long )(t * t) * 3) should have the same result as Y = ((long long)(VALUE * VALUE) * 3). Both are ‘12’ or ‘-4611686044197191668’. Because they have the same operation. Even if they produce an undefined behavior, the behavior should be the same.

Regards

Xing

Moderator
57 Views

Let us know if this is still an issue. Otherwise, we will close it.

Thanks,

Moderator
38 Views

We will no longer respond to this thread.

If you require additional assistance from Intel, please start a new thread. Any further interaction in this thread will be considered community only.

Thanks,