2
#include <stdio.h>
#include <math.h>

int main() {
    printf("%d\n", pow(3546, 0));   
    return 0;
}

The above code prints value 0

While the below code prints value 1

#include <stdio.h>
#include <math.h>

int main() {
    int a  = pow(3546, 0);
    printf("%d\n", a);
    return 0;
}

Why is it so? Even though they are equivalent.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    Does this help? [How is conversion of float/double to int handled in printf?](https://stackoverflow.com/questions/2398791/how-is-conversion-of-float-double-to-int-handled-in-printf) – Abra Apr 08 '20 at 02:37
  • second code would not compile because of a small typo, otherwise we would need more info regarding compiler and compiler flags to help you. The only hint we could have is to what type of integer the result of pow get *implicitly typecasted* into. even then most compiler with a minimum amount of optimization would see that the pow expression is constant and convert it in the compilation process. – dvhh Apr 08 '20 at 02:38
  • The first one is undefined behaviour which means anything can happen (and there is no requirement of consistency) – M.M Apr 08 '20 at 03:25

3 Answers3

1

There's no equivalence. The latter function explicitly converts the number to int before printing, while the former results in UB by trying to print a floating point number using wrong format specifier -- you're lucky to get 0, you might have gotten an arbitrary number.

Please, turn on all warnings on your compiler, it should complain about using wrong format for the wrong type of data.

lenik
  • 23,228
  • 4
  • 34
  • 43
  • The latter code neither contains any cast at all nor explicitly converts. A *cast* is an operator of the form `(type) operand`. In `int a = pow(3456, 0);`, there is no cast, and there is no explicit conversion. The conversion is implicit due to rules in the C standard; it is not explicitly stated in the code. (The explicit `int` there states the type of `a`; that is implicitly used for the conversion.) – Eric Postpischil Apr 08 '20 at 03:31
0

pow returns a result of type double, and %d is not a correct conversion specifier to use to format it. The resulting behavior is not defined by the C standard.

To print a double, you can use %g or %f, among other options:

printf("%g\n", pow(3456, 0));
printf("%f\n", pow(3456, 0));

In int a = pow(3546,0);, the double value is automatically convert to an int for the initialization of the int a, and then printing that int with %d is correct.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • @chqrlieforyellowblockquotes: I suspect one is a revenge vote by a disgruntled participant. I do not know about the other. I generally do not worry about it. I am not contributing for the points and have enough real-world points that imaginary Internet points are not a concern. I contribute to share good information and focus on that. – Eric Postpischil Apr 10 '20 at 22:38
  • Yes, I'm on the same page... Btw, what kind of computer did you program in Fortran back in 1975? I started in September of that year :) – chqrlie Apr 10 '20 at 23:10
  • @chqrlieforyellowblockquotes: Some IBM machine, but I am not sure which. System 360? – Eric Postpischil Apr 10 '20 at 23:15
-1

As mentioned in this answer, printf uses the format specifiers to know what to pop off the stack (%d is sizeof(int), which is 4 bytes in my system).

(Edit: As pointed out by rici in the comments, it's not guaranteed to be the stack (on x86 it's in the CPU registers most of the time). The standard never talks about implementations, which is what this problem is all about.)

A small fun fact: in my Windows 10 box it was 0 and 1. In my Xubuntu VBox it was 1 and -2132712864. Therefore, printf is poping only 4 bytes, and the endianess of your machine is screwing the result. From pow(3) manual:

#include <math.h>

double pow(double x, double y);
float powf(float x, float y);
long double powl(long double x, long double y);

So, in order to get the correct result in both cases, make use of the correct printf format specifier:

double a = pow(3546, 0);

And:

printf("%lf\n", pow(3546, 0));

If you want to know more about variadic functions (the ones like printf, which take a variable amount of parameters), start by reading the API manual page.

#include <stdarg.h>

void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
Enzo Ferber
  • 3,029
  • 1
  • 14
  • 24
  • 2
    The quoted answer is not universally correct, because function calls are not required to use the stack to pass arguments. In fact, on x86 the stack is only used if there are quite a few arguments. Arguments are usually passed in registers, and here is the key: integers and floating point numbers go into *different registers*. – rici Apr 08 '20 at 03:02
  • @rici Yes, that's correct. However, for the sake of simplicity, I think this is the best way to answer a simple question. There are enough links for the curious mind to expand itself, IMHO. Do you have any suggestions on how to improve this _while_ maintaining simplicity? – Enzo Ferber Apr 08 '20 at 03:15
  • practically all of them insist on the use of a stack. But the C standard itself *never says anything about stacks*. I know that sounds like a technicality, but it's still somewhat useful to know. Honestly, I don't know where the curious mind is going to find that link, although I have tried in the past. – rici Apr 08 '20 at 03:19
  • How is it simple to introduce a concept that is unnecessary for the answer? The core concept is that `%d` is wrong for printing the `double` result of `pow` and `%lf` is correct. Say that, and say that the second version works because `int a = pow(3456, 0);` converts the `double` result of `pow` to `int`, and then the `printf` correctly uses `%d` to print an `int`. And do not throw a beginner into the variable arguments features; those will be confusing for them. – Eric Postpischil Apr 08 '20 at 03:25
  • @EricPostpischil Wrong. Compile time conversion does a better job than the `printf` conversion because of implementation details, not because of the conversions. Both are `double` -> `int`. And in both cases it's UB, cause you depend on implementation. – Enzo Ferber Apr 08 '20 at 03:27
  • @rici I added your observation to the answer. However, I think the downvote is too much, for I don't see why the answer is wrong. – Enzo Ferber Apr 08 '20 at 03:32
  • What? Who said anything about what is a “better job” than anything else? What compile-time conversion versus `printf` conversion are you talking about? And what implementation-dependent behavior do you think there is in `int a = pow(3546,0); printf("%d\n",a);`? – Eric Postpischil Apr 08 '20 at 03:34
  • @EnzoFerber: The Stack Overflow guidelines for voting are not just about whether an answer is right or wrong but also about helpful, sloppy, effort, and such. Even if they were just about right and wrong, this answer is wrong as a matter of factor because “`printf` uses the format specifiers to know what to pop off the stack” is false in many C implementations. – Eric Postpischil Apr 08 '20 at 03:36
  • @EricPostpischil The compiler (implementation) will have to decide how to emit instructions for the _implicit_ cast in `int a = pow(...)` since `pow` will return a `double`. So a cast will be made, you just don't see it explicitly. And then `printf` may have a different mechanism to convert the values (since it's implementation defined, it may be stack, registers or something else, as rici pointed out). In both cases you are casting a `double` to an `int`, it doesn't matter if you use the explict `(type) object` notation or not. – Enzo Ferber Apr 08 '20 at 03:38
  • A *conversion* will be made, not a *cast*. These terms are well defined in the C standard. The conversion will be made, and the result is fully defined by the C standard; it will produce 1 in this case. There is no implementation-dependent behavior for that. How `printf` handles it is irrelevant; `printf("%d\n",a);` will then print “1”, regardless of the implementation. Whether any conversions are performed at compile time, in the executable code, or in `printf` is irrelevant. – Eric Postpischil Apr 08 '20 at 03:42