Intel® C++ Compiler
Community support and assistance for creating C++ code that runs on platforms based on Intel® processors.

Same operation, different results

qu__xing
Beginner
1,625 Views

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

0 Kudos
17 Replies
jimdempseyatthecove
Honored Contributor III
1,625 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

0 Kudos
qu__xing
Beginner
1,625 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

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,625 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

0 Kudos
Vladimir_P_1234567890
1,625 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? 

Vladimir

0 Kudos
qu__xing
Beginner
1,625 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.

0 Kudos
qu__xing
Beginner
1,625 Views

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

Xing

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,625 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

0 Kudos
Vladimir_P_1234567890
1,625 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

Vladimir

0 Kudos
Vladimir_P_1234567890
1,625 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.

Vladimir

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,625 Views

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

0 Kudos
Vladimir_P_1234567890
1,624 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;
}

Vladimir

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,624 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

0 Kudos
Vladimir_P_1234567890
1,624 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.

0 Kudos
qu__xing
Beginner
1,624 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

0 Kudos
qu__xing
Beginner
1,624 Views

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

Xing

0 Kudos
Viet_H_Intel
Moderator
1,408 Views

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


Thanks,


0 Kudos
Viet_H_Intel
Moderator
1,389 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,


0 Kudos
Reply