Mixing ANSI and K & R 1 code.

Dave Ciemiewicz ciemo at bananapc.wpd.sgi.com
Mon Nov 19 06:34:28 AEST 1990


In article <9011171557.AA16135 at karron.med.nyu.edu>, karron at KARRON.MED.NYU.EDU writes:
|> >>Seems you can't have newstyle argument lists (ANSI) receiving parameters
|> >>from old styles,non prototyped code.
|> >>
|> >>I first tried to write all my new stuff in ANSI code, but it would
|> >>always get float (Coord) arguments wrong.
|> >
|> >...
|> >

|> 
|> Sgi has make a weak attempt to address this "standard" problem. There is a
|>  cc -float flag to stop default promotion to doubles in argument/parameter
|> lists. Just seems not to work they way I hoped in spliced code...
|> 

Mr. Karron, you are mistaken.  From the cc(1) manual page:

     -float
          Cause the compiler to never promote expressions of type float to
          type double . This option does not affect float function arguments,
          which are always promoted to double unless function prototypes are
          used.

As is explained, -float has nothing to do with C function prototypes.  -float
is useful for preventing K&R style float-to-double conversion in floating
arithmetic operations.  The following example illustrates the problem.

	float a, b, c;

	c = a * b;

In K&R C, a and b, which are float are first both promoted to double, the
a double precision multiply occurs, the result is converted to float and
then assigned to c.

When doing numerical computations with lots of data, the expense of all the
floating point conversions between float and double may outweigh the benefit
of the extra precision.  This is especially true when doing dynamic 3D
graphics where a fast qualitative result may be a higher priority than
quantitative accuracy.

Invoking -float prevents the unneeded and expensive promotion to double in
the above example.

The ANSI C standard permits float arithmetic instead of requiring double.


|> I am not finished testing this out, so if anyone has any more insight, I
|> would like for you to speak up.
|> 
|> My design goal is to mix a library of K & R 1 code with ANSI C, and when the
|> K & R stuff is upgraded, not change my ANSI C code.
|>  Another suggested
|> solution is to make ANSI headers for K & R 1 code. This is a slippery issue
|> and I am not certain  about a robust answer. At least the run time code really
|> breaks when the problem arises ! (Only with floats).
|> 

Both you and the other gentlemen are probably falling into the following
trap.  This is not the result of a "weak attempt" on SGI's part to conform
to standards but a problem inherent in trying to mix K&R C with ANSI C.

If you have a library function coded as:

	float func(a, b)
	    float a;
	    float b;
	{
	    . . .
	}

you are using old style K&R C function declarations.  If you provide a
prototyped extern declaration, although it may be counter intuitive,
you should declare the prototype as:

	extern float func(double, double);

Why?  Well in the presence of K&R style coding of the function, the C
compiler must assume that you want K&R style float argument passing.  That
is, the call site must promote float arguments to double and then the called
function must convert the double arguments back to float.  This is most
assuredly the case when working with precompiled libraries built without
the use of function prototypes.  The above extern function prototype is
actually telling it like it is with K&R C style argument passing.


|> I guess that there is no performance penalty for passing by value floats
|> as doubles, but I am not certain of it. It is more bytes to push to the
|> stack.

Given the example above, do the call:

	float x, y, z;
	. . .
	
	z = func(x, y);

There is more than just the penalty of pushing twice as many bytes around
on the stack.  There also is the penalty of promoting x and y to double and
than back to float again inside func for a and b, as mentioned above.  These
conversions are extra cycles that are wasted.

By doing the ANSI C prototype conversion:

	extern float func(float, float);

	float func(float a, float b)
	{
	    . . .
	}

You are doing more than getting type checking, you telling the compiler to
be more efficient by eliminating 4 unnecessary type conversions, per function
call, 2 at the call site and 2 inside the function.

One last gotcha in this scenario is that once the function is implemented
with the prototype form, all call sites must agree on the calling conventions.
In this case, if you declare the function as:

	float func(float a, float b)
	{
	    . . .
	}

but call the function in an old K&R style module as:

	extern float func();

	float x, y, z;

	z = func(x, y); /* Fails because compiler promotes unknowingly
			   promotes x & y to double */

or without the benefit of an extern declaration, the call site will assume
K&R style function calls and thus fail.  The way to remedy this is to make
sure you have the ANSI C prototype extern declarations in header files for
all functions that you have changed to the ANSI C prototype convention.
These files must be appropriately included in each module which calls the
corresponding functions.

I hope this helps with the understanding of the issues involved in mixing
K&R C and ANSI C.

						--- Ciemo



More information about the Comp.sys.sgi mailing list