Archived

This topic is now archived and is closed to further replies.

Yet another printf question.

This topic is 5870 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I run this little program:
      
#include "stdafx.h"
#include <stdio.h>


int main(int argc, char* argv[])
{
	float a = 0.5f;
	int *b = (int*)&a;
	printf ("%f %f\n", a, *b);
	return 0;
}
      
And on the console I get "0.5000000 0.0000000". What am I doing wrong? Why don't I get an "0.5000 0.5000"? Does my VC++ have a bug or did I missunderstand functions with ... arguments? All of this is a debug compile of a Win32 console app, no optimisations. Edited by - Diodor on November 16, 2001 11:07:56 AM

Share this post


Link to post
Share on other sites
Or typecast the int in the printf function to type float by using:

printf("%f %f\n", (float)a, (float)*b);

if you require ints

ICQ: 130925152
Email: e.j.folkertsma@student.utwente.nl

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
quote:
Original post by Dark
Or typecast the int in the printf function to type float by using:

printf("%f %f\n", (float)a, (float)*b);

if you require ints

ICQ: 130925152
Email: e.j.folkertsma@student.utwente.nl


Did you actually try that before you posted it? ''Cause it doesn''t work.

Share this post


Link to post
Share on other sites
nickm: that does work. "*((float *)b)" to be formal.

what''s really wierd is if you type the line "printf("%f %f\n", (*b), f);" the display is "0.000 0.000". i even tried with "long" instead of "int".

To the vast majority of mankind, nothing is more agreeable than to escape the need for mental exertion... To most people, nothing is more troublesome than the effort of thinking.

Share this post


Link to post
Share on other sites
ANSI C specifies that a "long" must be at least 32 bits. an "int" is usually the bit range of the processor (i.e. Pentium = 32-bit processor). back in the "286" days, most compilers treated an "int" as 16-bit.

To the vast majority of mankind, nothing is more agreeable than to escape the need for mental exertion... To most people, nothing is more troublesome than the effort of thinking.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Uh...

Not to spoil your fun or anything... but int''s don''t store values past the decimal place. AFAIK they just ignore it.

So your program is working correctly.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I''m AP above.

Also, the size of int is dependent of the Operating system not the CPU.

Share this post


Link to post
Share on other sites
I''ll try to explain better this time. I also changed the program a bit:

  
#include <stdio.h>


int main(int argc, char* argv[])
{
float a = 0.5f;
printf ("%f %f\n", a, *(int*)&a);
return 0;
}



When calling printf, I pass it two params. One is a float value: a, the other one is the memory location of a, but as an int, not a float. a and *(int*)&a are the same memory location, and obviously, have the same 32 bits. (btw, I compiled in win32 console mode, so int is 4 bytes, as is float - I''ve double checked this).

Now from my understanding, the compiler should push both a and *(int*)&a on the stack, whether they are ints and floats. Then printf function would pop from the stack two floats with the var_arg macros (floats because of the "%f %f\n"). But because both 32 bits values pushed on the stack come from the same memory location and are identical, printf should print 0.50000 two times.

Still, I get 0.500000 0.00000.


If you still believe that there is any kind of rounding anywhere in the program:

  
#include <stdio.h>


int main(int argc, char* argv[])
{
float a = 0.5f;
int* b = (int*)&a;
int c = *b;
float d = *(float*)&c;
printf ("%f\n", a);
printf ("%f\n", c);
printf ("%f\n", d);
return 0;
}


The result is :
0.5000000
0.0000000
0.5000000

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
oh come on, how difficult is this to figure out? since you are explicitly casting your float type to an int, the decimal portion will be truncated, since your value is 0.5, it becomes 0. this is why c is a semi-type safe language. what you are doing doesn''t just put the same 32 bits on the stack, what you want to do is possible at the assembly language level, but c was designed so that there was *some* level of protection for the programmer.

Share this post


Link to post
Share on other sites
Alright, can anyone explain why when I run the next program:

  
int main(int argc, char* argv[])
{
float a = 0.5f;
int* b = (int*)&a;
int c = *b;
float d = *(float*)&c;
printf ("%f\n", a);
printf ("%f\n", c);
printf ("%d\n", c);
printf ("%f\n", d);
return 0;
}


I get this output :

0.500000
0.000000
1056964608
0.500000

?

Everythink is OK except for the 0.000000 from the printf ("%f\n", c); line.

Share this post


Link to post
Share on other sites
I was looking at the compiled asm, I understand what is happening.

Its pretty clear in the asm, but I'll explain anyway. printf needs to receive float's as double's, the 0.5f value is still being passed, it just not decoded because its not a double.

    
/*
00401029 C7 45 FC 00 00 00 3F mov dword ptr [ebp-4],3F000000h
3F000000h that's the hex value of 0.5f
its being stored at [ebp-4]
*/
float a = 0.5f;
/*
00401030 8D 45 FC lea eax,[ebp-4]
address of your float
00401033 89 45 F8 mov dword ptr [ebp-8],eax
its being stored at [ebp-8]
*/
int *b = (int*)&a;
/*
00401036 8B 45 F8 mov eax,dword ptr [ebp-8]
loads address of float
00401039 8B 08 mov ecx,dword ptr [eax]
loads float
0040103B 89 4D F4 mov dword ptr [ebp-0Ch],ecx
its being stored at [ebp-0Ch]
*/
int c = *b;
/*

0040103E 8B 45 F4 mov eax,dword ptr [ebp-0Ch]
loads your float
00401041 89 45 F0 mov dword ptr [ebp-10h],eax
its being stored at [ebp-10h]
*/
float d = *(float*)&c;
/*
00401044 D9 45 FC fld dword ptr [ebp-4]
load your float
00401047 83 EC 08 sub esp,8
0040104A DD 1C 24 fstp qword ptr [esp]
stores it as double on stack
0040104D 68 2C 61 42 00 push offset string "%f" (0042612c)
00401052 E8 79 00 00 00 call printf (004010d0)
00401057 83 C4 0C add esp,0Ch
*/
printf ("%f\n", a);
/*
0040105A 8B 45 F4 mov eax,dword ptr [ebp-0Ch]
loads your float
0040105D 50 push eax
puts it on the stack
0040105E 68 2C 61 42 00 push offset string "%f" (0042612c)
00401063 E8 68 00 00 00 call printf (004010d0)
00401068 83 C4 08 add esp,8
*/
printf ("%f\n", c); //change this to printf ("%x\n", c); to see it in hex
/*
0040106B D9 45 F0 fld dword ptr [ebp-10h]
loads your float
0040106E 83 EC 08 sub esp,8
00401071 DD 1C 24 fstp qword ptr [esp]
stores it as double on stack
00401074 68 1C 60 42 00 push offset string "%d," (0042601c)
00401079 E8 52 00 00 00 call printf (004010d0)
0040107E 83 C4 0C add esp,0Ch
*/

printf ("%f\n", d);


Edited by - burp on November 16, 2001 4:24:30 PM

Share this post


Link to post
Share on other sites
Hello,

int* b = (int*)&a;

It is assigning b an integer pointer with the address of a(float) which is converted to an int pointer. (Fine)

b is an int pointer to a floats value

Then

printf ("%f %f\n", a, *b);

displays a correctly


*b displays the value stored in a(but only the number of bytes that an int uses) as that int value converted to a float value

Share this post


Link to post
Share on other sites
1056964608 == 0x3f000000 == 0.5f;
double(0.5f) == 0x3fe0000000000000 == 4602678819172646912





Edited by - burp on November 16, 2001 5:43:15 AM

Share this post


Link to post
Share on other sites
Thanks burp, you are right. I still don''t understand how come passing a float as an argument can result in a double being pushed on the stack. Seems to me like a bug of VC.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
quote:
Original post by Diodor
Thanks burp, you are right. I still don''t understand how come passing a float as an argument can result in a double being pushed on the stack. Seems to me like a bug of VC.


No, you just don''t understand casting and C''s typing yet. Give it time.

Share this post


Link to post
Share on other sites
quote:

No, you just don''t understand casting and C''s typing yet. Give it time.



That didn''t help. Could you explain why passing a float value (32 bit)to printf results in a double value (64 bit) being pushed on the stack?

Share this post


Link to post
Share on other sites
If you have the MS SDK, search for ...
quote:
MS SDK: If the actual argument is of type float, it is promoted to type double prior to the function call.


Here's a little version of printf I wrote, to show how it works

Edit... oops that's the wrong one

  
#include <stdio.h>
//

#include <stdlib.h>
//

#include <stdarg.h>
//

void burps_printf( const char *format, ... )
{
va_list l;
va_start( l, format );

while(*format != 0)
{
if(*format == '%')
{
switch(*++format)
{
//I wasn't going to re-write a int and double decodes

case 'd': // for ints

printf("%d",va_arg( l, int));
break;
case 'f': //for doubles

printf("%f",va_arg( l, double));
break;
}
}
else
{
putchar(*format);
}
format++;
}
va_end( l );
}
//

void main(void)
{
float a = 0.5f;
int b = 100;
burps_printf (" start\n %f\n %d\n %f\n end\n ", a, b, a);
}



Edited by - burp on November 16, 2001 4:39:33 PM

Share this post


Link to post
Share on other sites
  
void average( float first, ... )
{
float count = 0.0f, sum = 0.0f, i = first;
va_list marker;

va_start( marker, first );
while( i > 0.0f )
{
sum += i;
count += 1.0f;
i = va_arg( marker, double); // have to use double instead of floats

}
va_end( marker );
printf ("%f\n", sum / count);
}


Yes, after you gave me the double idea, I''ve made a var arg average function and it works well. So, is this a bug or a feature?

Share this post


Link to post
Share on other sites
quote:
So, is this a bug or a feature?

Both, printf is an old function from the early days of programming, use iostream its type save.
quote:
MS SDK: Ellipses denote that arguments may be required but that the number and types are not specified in the declaration. This is normally poor C++ programming practice because it defeats one of the benefits of C++: type safety

Share this post


Link to post
Share on other sites
HI

I made a post to your printf question.

However, another person burp seems to me has added a little bit of confusion.

Okay, there was no bug in printf. It did exactly what it was told too.

Telling it to print an integer value as a float just isn''t a good idea. When you expect a float value.

By referencing the float value thro an integer pointer you are passing an integer not a float.

Now I will admit that the complier nor printf will convert the integer value to a float value.

But Burp thinks that the integer value wasn''t decoded because it wasn''t a float or a double is wrong. When in fact in the assembler code he provided you can see it passed the integer value to prinf it just didn''t convert it to a double because it wasn''t a float.

I hope this clears the air.

But if I am wrong please explain Burp''s theory to me.

Thanks

Share this post


Link to post
Share on other sites