Strange behaviour with old-style code

Steve Summit scs at adam.mit.edu
Sat Feb 9 19:48:29 AEST 1991


In article <1991Feb6.190607.11731 at alias.uucp> rsargent at alias.UUCP (Richard Sargent) writes:
>I have come across a compiler which does something very unusual
>in its handling of old-style functions.
>The compiler passes floats widened to doubles, and the routine pulls
>the values off the stack using the appropriate widened addressing,
>but then proceeds to use the "declared" argument type to do single
>precision floating point arithmetic.

Actually, it looks like the compiler is behaving correctly.  If
you thought that the compiler should treat

	bar(u)
	float u;
	{
	...

"as if" u had been declared as a double, that is exactly what the
compiler is _not_ supposed to do.  The compiler is instead
supposed to do the equivalent of

	bar($u)
	double $u;
	{
	float u = $u;
	...

where $u is just a pseudo-name for the passed value, on the stack
or wherever.  As you may have suspected, the distinction is
significant if the address of u is passed to some other routine
which expects a pointer-to-float.

> A colleague of mine has
> crawled through the Standard as well as K&R II, but found nothing
> that explicitly addresses this issue.

X3.159, section 3.7.1, p. 82, lines 21-22: "On entry to the
function the value of each argument shall be converted to the
type of its corresponding parameter, as if by assignment to the
parameter."  The Rationale reasserts this, and explicitly states
that "Type rewriting [i.e. pretending that float arguments had
been declared double] is no longer permissible."

K&R2 spells this out in some fine print in section A10.1, on page
226:

	"There is... a small change in the details of promotion:
	the first edition specified that the declarations of
	float parameters were adjusted to read double.  The
	difference becomes noticeable when a pointer to a
	parameter is generated within a function."

The distinction is actually between the old interpretation of
old-style function definitions and the new interpretation of
those old definitions.  Your compiler appears to be correctly
following the new interpretation.  (I say "appears" because it
correctly reports sizeof(u) as 4.  There is a subtle, secondary
bug which is possible, depending on the machine's floating-point
formats.  Given a fortuitous byte order and bit arrangement, it
is often possible, or at least appears possible, essentially to
skip the local allocation and assignment

	float u = $u;

and generate code which treats the stack location $u as if it
were a float.  This works if the bits and bytes making up a float
are an initial subset of those making up a double, with the added
bits in a double simply adding precision.  For example, VAX F_
and D_floating formats satisfy this relationship.  However, the
Rationale warns that "Not many implementations can subset the
bytes of a double to get a float.  Even those that apparently
permit simple truncation often get the wrong answer on certain
negative numbers."  I am sure that someone else will expand on
this point if it is important.)

Note, by way of contrast, that since "Array expressions and
function designators as arguments are converted to pointers
before the call," it _is_ the case that "A declaration of a
parameter as `array of type' shall be adjusted to `pointer to
type,' and a declaration of a parameter as `function returning
type' shall be adjusted to `pointer to function returning type'".
(X3.159 section 3.7.1 again.)  That is, float (and char and
short) parameter declarations are not "rewritten," while array
and pointer parameters are.

                                            Steve Summit
                                            scs at adam.mit.edu



More information about the Comp.std.c mailing list