1

I was reading this question on SO. After reading the first answer I was unable to understand the reason for -5 >> 1 = -3. I also tweaked a little bit more around it.

You can also see the code and output here. Here is what I did:

#include<stdio.h>

int main(){

printf("5/2 = %d\n",5/2);
printf("5 >> 1 = %d\n",5 >> 1);
printf("5/2 = %lf\n",5/2);
printf("5 >> 1 = %f\n",5 >> 1);
printf("-5/2 = %d\n",-5/2);
printf("-5 >> 1 = %d\n",-5 >> 1);
printf("-5/2 = %f\n",-5/2);
printf("-5 >> 1 = %f\n",-5 >> 1);

return 0;
}

Output :

5/2 = 2
5 >> 1 = 2
5/2 = 2.168831
5 >> 1 = 2.168831
-5/2 = -2
-5 >> 1 = -3
-5/2 = 2.168833
-5 >> 1 = 2.168833

I am unable to understand 5/2 == 2.168831, 5 >> 2 == 2.168831, 5 >> 1 == -3.

Why this is happening? (It may be possible that the answer is very basic and I am missing some basic things, so pls guide me).

Community
  • 1
  • 1
Abhishek Gupta
  • 6,465
  • 10
  • 50
  • 82
  • 3
    C11 Final Draft, §6.5.7: "*The result of E1 >> E2 is E1 right-shifted E2 bit positions. ... If E1 has a signed type and a negative value, the resulting value is implementation-defined.*" C11 Final Draft, §7.21.6.1: "*If a conversion specification is invalid, the behavior is undefined. If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.*" – DCoder Jan 13 '13 at 10:40

2 Answers2

9

The result of -5 / 2 is an int, not a float or a double. However your format specifier is %f, so your int gets interpreted as a float, which makes no sense, hence the erratic values. What you are doing is called undefined behavior: anything can happen.

user703016
  • 37,307
  • 8
  • 87
  • 112
  • But, why this error is consistent always. What I am not getting how this particular value is coming. Putting `%f` was intentional – Abhishek Gupta Jan 13 '13 at 10:34
  • 2
    It *happens* to be. This is undefined behavior: anything could happen. There is no point in trying to make sense out of the result. For example, on my machine, I get `0` instead of `2.168831`. – user703016 Jan 13 '13 at 10:40
  • 1
    It is not correct to state there is no point in trying to make sense of the result. Understanding why the computer behaves this way will not help one understand C, but it can help one understand computers, the low-level mechanisms of passing arguments, and how compilers are implemented. This knowledge is important and can be enlightening to students struggling to understand the complexities of computing. There is no reason people should be expected to learn C only as an abstraction and not learn how we construct that abstraction. – Eric Postpischil Jan 13 '13 at 11:20
  • @EricPostpischil There is **absolutely** no point trying to make sense of the result of *undefined behavior*. The program could very well order a pizza online for all you know. What is interesting is *understanding* how `printf` works and what *causes* UB. – user703016 Jan 13 '13 at 11:46
  • 1
    Point: If one learns why printf in a particular implementation prints the value it does, one learns how parameters are passed in that implementation’s ABI. Point: If one learns why different implementations print different values, one learns something about why the C standard is designed as it is, to leave some things undefined. Point: If one learns how parameters are passed, one learns about stacks and processor registers. Point: If one learns about how a C implementation works, one learns a little of what is needed to make a C implementation. – Eric Postpischil Jan 13 '13 at 11:52
  • 1
    Doesn't %f format interpret parameter as a double rather than a float? (hint: in absence of signature specification, float arguments of a variadic function have to be promoted to double). If sizeof int == sizeof double, and arguments pushed onto stack, then behavior (reinterpret_cast) could be considered as implementation defined. But since sizeof(int) is 4, and sizeof(double) 8 on great majority of today's architectures, behavior also depend on endianness, alignment, arguments order... In worst case, it depends on what was on the stack before printf get executed... Thus undefined behavior. – aka.nice Jan 13 '13 at 12:51
3

The reason you see the results you do is:

When you pass an int argument but use a printf specifier for double (remember that a float is converted to a double in this situation), then most C implementations pass the int argument according to their usual rules for passing a int argument to a variadic function (a function that accepts diverse argument types), but the printf routine interprets the machine state as if it were passed a double argument, as described below. (This is not necessarily what always happens; once you leave the behavior defined by the C standard, a C implementation may do other things. In particular, there can be complex interactions with the optimizer that cause surprising results. However, this is what happens most commonly. You cannot rely on it.)

Each computing platform has some rules for how arguments are passed. One platform might specify that all arguments are pushed onto the stack, from right to left, and that each argument is put onto the stack using only as many bytes as it needs. Another platform might specify that arguments are pushed onto the stack left to right or that arguments are padded up to the next multiple of four bytes, to keep the stack nicely aligned. Many modern platforms specify that integer arguments under a certain size are passed in general registers, floating-point arguments are passed in floating-point registers, and other arguments are passed on the stack.

When printf sees %f and looks for a double argument, but you have passed an int, what does printf find? If this platform pushes both arguments onto the stack, then printf finds the bits for your int, but it interprets those bits as if they were a double. This results in printf printing a value determined by your int, but it bears no obvious relationship to your int because the bits have entirely different meanings in the encodings for int and for double.

If this platform puts an int argument in one place but a double argument in another place, then printf finds some bits that have nothing at all to do with your int argument. They are bits that just happened to be left over in, for example, the floating-point register where the double argument should be. Those bits are just the residue of previous work. The value you get will be essentially random with respect to the int you have passed. You can also get a mix, with printf looking for eight bytes of a double by taking four bytes of the int you passed along with four bytes of whatever else was nearby.

When you run the program multiple times, you will often see the same value printed. This happens for two reasons. First, computers are mechanical. They operate in largely deterministic ways, so they do the same things over and over again, even if those things were not particularly designed to be used the way you are using them. Second, the environment the operating system passes to the program when it starts is largely the same each time you start the program. Most of its memory is either cleared or is initialized from the program file. Some of the memory or other program state is initialized from other environment in the computer. That data can be different from run to run of the program. For example, the current time obviously changes from run to run. So does your command history, plus values the command shell has placed in its environment variables. Sometimes running a program multiple times will produce different results.

When you use code whose behavior is not defined by some specification (which may be the C specification, a compiler specification, a machine and operating system specification, or other documents), then you cannot rely on the behavior of that code. (It is possible to rely on the behavior of code compiled by a particular C compiler that is specified by that C compiler even though it is not fully specified by the C standard.)

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312