Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the output of `printf("%llu\n", 1ull << n);` and `printf("%llu\n", 1ull << 64);` is different in C++? (n=64) [duplicate]

Why the output of printf("%llu\n", 1ull << n); and printf("%llu\n", 1ull << 64); is different in C++?

Code:

#include <cstdio>

int main()
{
    int n = 64;
    printf("%llu\n", 1ull << n);
    printf("%llu\n", 1ull << 64);
    return 0;
}

Output:

1
0
like image 915
PhoenixGS Avatar asked Dec 03 '19 10:12

PhoenixGS


People also ask

How does printf work in C language?

It works the same way as “printf” works in programming languages like C. Note: printf can have format specifiers, escape sequences or ordinary characters. Format Specifiers: The most commonly used printf specifiers are %s, %b, %d, %x and %f.

What is the return value of printf in C?

Now, printf always returns an integer value, which is the number of characters it successfully printed. In this case, that call to printf will return 6, the number of characters in the string “printf”. So, the data type of the second argument to the outer printf is an int, and the value of that argument will be 6.

What does the printf () function not print?

Note − It does not print anything. Another printf () function is used to print the statement. Even if we give the value to the identifier, it will not consider the value given by us.

How many characters does printf print in C?

Now, printf always returns an integer value, which is the number of characters it successfully printed. In this case, that call to printf will return 6, the number of characters in the string “printf”.


3 Answers

See here:

In any case, if the value of the right operand is negative or is greater or equal to the number of bits in the promoted left operand, the behavior is undefined.

If your left operand is 64 bit and your right operand is 64, this is undefined behavior, and then anything can happen without any guarantee of consistency.

Your compiler should also issue a warning for that, at least it does for me when I try it with GCC or Visual Studio.

like image 142
Blaze Avatar answered Oct 22 '22 15:10

Blaze


The reason is that expressions like 1<<64 are compile time constants and are indeed computed by the compiler at compile time. No code to shift anything is emitted.

The expression 1<<64 is evaluated by the compiler as 0, which is plausible and legit since the behavior is actually, as others have pointed out, undefined. The produced assembly for uint64_t i = (uint64_t)1 << 64; is simply to store zero in the variable's location:

QWORD PTR [rbp-16], 0

Now, for a non-compile time value code is emitted. uint64_t i2 = (uint64_t)1 << n; translates to

    mov     rax, QWORD PTR [rbp-8]
    mov     edx, 1
    mov     ecx, eax
    sal     rdx, cl
    mov     rax, rdx
    mov     QWORD PTR [rbp-24], rax

All the boilerplate code before and after the actual SAL shift instruction is just moving the operands in place and moving the result into the variable. The important thing is that the compiler indeed emits code to shift the 1 here. Because shifting by more than 63 is illegal and pointless for 64 bit values Intel processors silently mask the shift value:

REX prefix in the form of REX.W [I must assume that that happens here] promotes operation to 64-bits and sets the mask width for CL to 6 bits.

That is, the processor internally masks n's value of 64/100'0000 with 63/11'1111, resulting in a shift value of 0. The result is, of course, the original 1.

With higher optimization levels the compiler optimizes that instruction away as well because it can infer the value of the non-volatile n, and emits 0 there as well.

like image 20
Peter - Reinstate Monica Avatar answered Oct 22 '22 15:10

Peter - Reinstate Monica


From the C Standard (6.5.7 Bitwise shift operators)

3 The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined

So the program has undefined behavior.

The difference of the output can be explained the following way that the compiler generates different object code when an integer constant (literal) is used compared with the code when a variable is used.

like image 2
Vlad from Moscow Avatar answered Oct 22 '22 15:10

Vlad from Moscow