Consider this program
int main()
{
float f = 11.22;
double d = 44.55;
int i,j;
i = f; //cast float to int
j
The printf function uses the format specifiers to figure out what to pop off the stack. So when it sees %d, it pops off 4 bytes and interprets them as an int, which is wrong (the binary representation of (float)3.0 is not the same as (int)3).
You'll need to either use the %f format specifiers or cast the arguments to int. If you're using a new enough version of gcc, then turning on stronger warnings catches this sort of error:
$ gcc -Wall -Werror test.c
cc1: warnings being treated as errors
test.c: In function ‘main’:
test.c:10: error: implicit declaration of function ‘printf’
test.c:10: error: incompatible implicit declaration of built-in function ‘printf’
test.c:10: error: format ‘%d’ expects type ‘int’, but argument 4 has type ‘double’
test.c:10: error: format ‘%d’ expects type ‘int’, but argument 5 has type ‘double’
Response to the edited part of the question:
C's integer promotion rules say that all types smaller than int get promoted to int when passed as a vararg. So in your case, the 'd' is getting promoted to an int, then printf is popping off an int and casting to a char. The best reference I could find for this behavior was this blog entry.