Date: Sun, 20 Nov 94 22:57:53 GMT From: dolan AT fnoc DOT navy DOT mil (Kent Dolan) To: dj AT stealth DOT ctron DOT com Subject: Re: printf or floating point error????? Cc: davis AT amy DOT tch DOT harvard DOT edu, djgpp AT sun DOT soe DOT clarkson DOT edu KPD> long i = 10 ; KPD> float x , y , z ; KPD> double w ; KPD> KPD> printf( "i=%8.8XH, x=%8.8XH, y=%8.8XH, z=%8.8XH\n", i, x, y, z ); KPD> --------------------------8<---cut here--->8------------------------------- KPD> long is: 4 bytes, float is: 4 bytes, double is: 8 bytes. KPD> i=0000000AH, x=40240000H, y=00000000H, z=40240000H KPD> i=10, x=10, y=10, z=1.4013e-44 KPD> -------------------------8<---cut here--->8------------------------------- KPD> Huh? KPD> Variables x and z hold the same bit pattern, but x and y give the same KPD> results? DJD> ANSI states that floats are promoted to double when passed to a DJD> function with a formal prototype type of "...". Thus, you are passing DJD> 8 bytes per float, not 4, and taking up two %X per float instead of DJD> just one. You need to use this: DJD> *(int *)(&x) DJD> to pass only the four bytes of the float variable itself. Once more into the breach: -------------------------------8<---cut here--->8------------------------------- #include #include main() { long i = 10 ; float x , y , z ; double w ; x = i ; y = (float) i ; z = *(float *) &i ; printf( "long is: %d bytes, float is: %d bytes, double is: %d bytes.\n", sizeof( long ), sizeof( float ), sizeof( double ) ); /* 1) what reads in the C language like it should work: */ printf( "1) i=%8.8XH, x=%8.8XH, y=%8.8XH, z=%8.8XH\n" , i , x , y , z ); /* 2) does it behave differently if we only put one variable at a time on the stack? */ printf( "2) i=%8.8XH, " , i ); printf( "x=%8.8XH, " , x ); printf( "y=%8.8XH, " , y ); printf( "z=%8.8XH\n" , z ); /* 3) DJ says the floats stack as doubles; do they come off the stack that way? */ printf( "3) i=%8.8XH, x=%8.8X%8.8XH, y=%8.8X%8.8XH, z=%8.8X%8.8XH\n", i, x, y, z ); /* 4) the obscenity actually needed to print the bit patterns we want to check: */ printf( "4) i=%8.8XH, x=%8.8XH, y=%8.8XH, z=%8.8XH\n", i, *(int *)&x, *(int *)&y, *(int *)&z ); /* the "g" type claims to print everything, but really doesn't. however, C type coercion does what C casts sometimes do, so this gets out the readable values in another funny way. */ printf( "5) i=%g, " , w=i ); printf( "x=%g, " , w=x ); printf( "y=%g, " , w=y ); printf( "z=%g\n" , w=z ); exit(0); } -------------------------------8<---cut here--->8------------------------------- Annotated: long is: 4 bytes, float is: 4 bytes, double is: 8 bytes. 0) confirm the variable's storage sizes. 1) i=0000000AH, x=40240000H, y=00000000H, z=40240000H We're getting parts of different variables than we think, because we're stacking 28 bytes into printf, but only unstacking the first 16. 2) i=0000000AH, x=40240000H, y=40240000H, z=36D40000H We're getting "left hand" parts of the the right variables, but not the original bits because a coercion to double has intervened, and not the whole coerced bit pattern because the coercion has changed the storage size. 3) i=0000000AH, x=4024000000000000H, y=4024000000000000H, z=36D4000000000000H We're getting all the bytes we stacked, but not the original bits of x, y, and z because of the type coercion to doubles. 4) i=0000000AH, x=41200000H, y=41200000H, z=0000000AH We've gotten the original bits, and solved the original poster's question, at the cost of counter-intuitive code. 5) i=10, x=10, y=10, z=1.4013e-44 Our expectations are fulfilled; pushing to the pointer to the variable and then reading back from the variable munges the floating point value severely. -------------------------------8<---cut here--->8------------------------------- Well, the behavior is worse than merely stacking an extra four bytes per float, apparently a double also uses a different exponent structure, because the original double bit pattern is nowhere to be seen in the printed floats. The answer is that C doesn't behave like it reads, so finding that a compiler doesn't do what you expect is not a big surprise, whether it is you or the compiler writer who got confused by the language's intended, rather than expected, behavior. The metaanswer is get a programming language that does, if you expect to write code that is safe to execute or debuggable in a reasonable amount of time. That's why the world's most successful language, COBOL, reads like English. Bank auditors wanted a language that behaves like it reads, for purposes of avoiding unexpected behavior, like someone siphoning off funds by passing a variable with their account number hidden in it by behavior like the above. That's why, in Modula-2, the equivalent of printf() is one of the things Wirth omitted deliberately and with commentary, for just the reasons of the results shown above. If you want to print ten items, you use ten write statements, each of which writes exactly one kind of data with no type coercion. Sigh. This isn't a big putdown of C, though I'm amazed ever again how easy it is to forget how hostile the language is and what the details are of making things do what you want instead of what you say. I have quite a bit of contributed software on the net that I wrote in C, some of it nearly good enough to land me a job with Electronic Arts one job hunt back (townmaze(), written in response to a query in one of the fantasy role playing game groups, designs town layouts like the top level of Bards Tale). I used to be the net's maintainer of the Unix version of lharc(). I wrote an interactive research grant proposal budget creator in C. Once upon a time, ten years back, I wrote a set of what would now be called modules, probably, in C, for other programmers to use without worrying about the details, to convert 40 gigabytes of shipyard mag tape data, one bit at a time, from Honeywell to IBM mainframe internal data representations. And lots of other stuff. And still I can't read the language. Take a lesson from my sad situation. Have a new occupation waiting when you pass 50, or learn a language with programmer-friendly behavior and stick to it. At home I write Modula-2 and do really fun stuff in quick time coding. At work ..., no, you don't want to know. Oh, and my aplogies to the original poster. The compiler seems to be broken. Xanthian. -- Kent, the man from xanth. Kent Paul Dolan, CSC contractor at Fleet Numerical. (408) 656-4363. (Navy Unix email: ) (Navy cc:Mail email: ) (real world email: )