The C programming language does not perform type checking in many
situations including this one when parameters are passed through a
"..." prototype. So the (float)3/2 is passed as a real floating
parameter.
Right. Furthermore, an argument of type float is promoted to type
double when passed through a "..." prototype.
However, "%d" causes the library function to read the
parameter as if it were an int. The result is kind of like doing
this:
float x = 1.0;
int y = *((int *)&x);
which just reads memory that's representing a floating point as if it
were an integer, which doesn't have any defined meaning in ANSI C.
It's kind of like that, but worse. The "%d" tells printf to read an
int argument; the double argument that you actually passed isn't
necessarily even in the same place that the int argument would have
been. For example, the calling convention might use different
registers for integer and floating-point arguments. (That's not too
likely in this case, since variadic arguments are typically passed in
the same way regardless of type (typically on "the stack" for machines
that use a stack), but the standard allows it.)
The bottom line is that it's undefined behavior; the solution is not
to do that.