- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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

- Tags:
- CC++
- Development Tools
- General Support
- Intel® C++ Compiler
- Intel® Parallel Studio XE
- Intel® System Studio
- Optimization
- Parallel Computing
- Vectorization

Link Copied

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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?

Vladimir

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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 12When 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.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

Vladimir Polin (Intel) wrote: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?

Vladimir

So is this an optimization problem for the icc compiler?

Regards

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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

Vladimir

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

qu, xing wrote:So is this an optimization problem for the icc compiler?

Regards

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.

Vladimir

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

Vladimir,

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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; }

Vladimir

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

Thanks for your explanation.

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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

Thanks,

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content

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,

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page