www.delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/1994/11/20/22:47:19

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 <stdio.h>
#include <stdlib.h>

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:     )
<dolan AT fnoc DOT navy DOT mil>  <dolank AT fnoc DOT navy DOT mil>  <xanthian AT well DOT sf DOT ca DOT us>

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019