[comp.lang.c] Nasty bug

e89hse@rigel.efd.lth.se (08/30/90)

Hi !

 I had a lot of trouble with a bug yesterday. The code was similar to the
following:

void main()
{
    prnval("10.0",0.0);
    exit(0);
}

prnval(s,f)
char   *s;
float   f;
{
   if(f == 0.0)
       sscanf(s,"%f",&f);
   printf("%10.2f\n",f);
}

And it didn't work. Why? The answer is that the parameter f is a
double, not a float since all floats are converted to double when they are
passed as arguments to functions. Therefore &f is a ptr to double rather than a
ptr to float as one would expect looking at the declartion. Later I rewrote
prnval() as:

prnval(s,f)
char *s;
double f;
{
   if(f == 0.0)
       sscanf(s,"%lf",&f);
   printf("%10.2f\n",f);
}

 Well, I hope I'll help someone to avoid that problem... It took me a while to
figure out why the code didn't work...

 Henrik Sandell

Disclaimer: I know that this code wouldn't pass lint without warnings, but that
is not the purpose of the code...

pierre@rhi.hi.is (Kjartan Pierre Emilsson) (08/30/90)

From article <0093BF08.7F3834E0@rigel.efd.lth.se>, by e89hse@rigel.efd.lth.se:
> Hi !
> 
>  I had a lot of trouble with a bug yesterday. The code was similar to the
> following:
> 
> [Code deleted] 
>	prnval(s,f)
>	char *s;
>	float f;
> 
> And it didn't work. Why? The answer is that the parameter f is a
> double, not a float since all floats are converted to double when they are
> passed as arguments to functions.Therefore &f is a ptr to double rather than
> a float.
> 
>  Henrik Sandell

I don't know what other think, but as a relatively experienced C programmer, I
find this bug very counter-intuitive, being used to think that when I declare
some variable in code to be of a given type, I certainly want it to be
of that given type, regardless of what crap I choose to put in it later.
From that point of view, float variables should not be allowed in function
argument declaration.  Personally I never use floats, but this is certainly
a *very* nasty bug.


			-Kjartan
			
-------------------------------------------------------------------------
Kjartan Pierre Emilsson
Science Institute of the University of Iceland
Dunhaga 3
Reykjavik
ICELAND				e-mail: pierre@krafla.rhi.hi.is

wallace@ynotme.enet.dec.com (Ray Wallace) (08/30/90)

In article <0093BF08.7F3834E0@rigel.efd.lth.se>, e89hse@rigel.efd.lth.se
writes... 
> I had a lot of trouble with a bug yesterday. The code was similar to the
>following:

>And it didn't work. Why? The answer is that the parameter f is a
>double, not a float since all floats are converted to double when they are
>passed as arguments to functions. Therefore &f is a ptr to double rather than a
>ptr to float as one would expect looking at the declartion.

Sounds like a compiler bug to me. Floats are promoted to double in a procedure
call but since your procedure declared the parameter as a float the compiler
should have "demoted" the parameter so that &variable would work properly.

FYI: Your code fragment worked properly with the VAX VMS-C compiler.

---
Ray Wallace		
		(INTERNET,UUCP) wallace@oldtmr.enet.dec.com
		(UUCP)		...!decwrl!oldtmr.enet!wallace
		(INTERNET)	wallace%oldtmr.enet@decwrl.dec.com
---

walter@hpcllca.HP.COM (Walter Murray) (08/30/90)

Henrik Sandell writes:

>  I had a lot of trouble with a bug yesterday. The code was similar to the
> following:

[Program using an old-style (non-prototype) function definition with
a parameter of type float]

>And it didn't work. Why? The answer is that the parameter f is a
>double, not a float since all floats are converted to double when they are
>passed as arguments to functions. Therefore &f is a ptr to double rather than a
>ptr to float as one would expect looking at the declartion.

> Well, I hope I'll help someone to avoid that problem... It took me a while to
>figure out why the code didn't work...

You got bit by what is called type rewriting, something that was done by
some pre-ANSI compilers.  Although you declared the parameter as float,
the compiler acted exactly as if you had declared it as double.  Some
compilers may also do this with parameters of type char or short, pretending
that you had actually declared them as int.

Type rewriting is not permitted for a compiler that conforms to the
ANSI standard.  A program that relies on type rewriting may not behave
as expected when compiled with a standard-conforming compiler.  For
more information, see section 3.7.1 of the ANSI C Rationale.

Walter Murray
------

volpe@underdog.crd.ge.com (Christopher R Volpe) (08/31/90)

In article <0093BF08.7F3834E0@rigel.efd.lth.se>, e89hse@rigel.efd.lth.se
writes:
|>void main()
|>{
|>    prnval("10.0",0.0);
|>    exit(0);
|>}
|>
|>prnval(s,f)
|>char   *s;
|>float   f;
|>{
|>   if(f == 0.0)
|>       sscanf(s,"%f",&f);
|>   printf("%10.2f\n",f);
|>}
|>
|>And it didn't work. Why? The answer is that the parameter f is a
|>double, not a float since all floats are converted to double when they are
|>passed as arguments to functions. 

It's true that f is promoted to double in the printf, but that's ok
because printf expects a double when it sees %f. It knows that floats
never get passed just as floats. 

Also, the scanf is ok here because you're passing a pointer to 
a float, and the %f tells scanf in this case that the argument is
a pointer to a float, not a double. So, the printf and scanf are
both ok here. I would expect that the problem is being caused
not by printf or by scanf, but by the fact that the formal parameter
to PRNVAL is declared as a float, when it is REALLY being passed
a double from main(). (Anyone out there agree/disagree with this????)

|>Therefore &f is a ptr to double rather than a
|>ptr to float as one would expect looking at the declartion. 

No, &f is still a ptr to float, not a ptr to double. Floats get
promoted, pointers don't. This isn't the problem.

|>Later I rewrote
|>prnval() as:
|>
|>prnval(s,f)
|>char *s;
|>double f;
|>{
|>   if(f == 0.0)
|>       sscanf(s,"%lf",&f);
|>   printf("%10.2f\n",f);
|>}

This is the correct solution anyway.
|>
|> Henrik Sandell
                                          
==================
Chris Volpe
G.E. Corporate R&D
volpecr@crd.ge.com

volpe@underdog.crd.ge.com (Christopher R Volpe) (08/31/90)

In article <2020@engage.enet.dec.com>, wallace@ynotme.enet.dec.com (Ray
Wallace) writes:
|>
|>In article <0093BF08.7F3834E0@rigel.efd.lth.se>, e89hse@rigel.efd.lth.se
|>writes... 
|>> I had a lot of trouble with a bug yesterday. The code was similar to the
|>>following:
|>
|>>And it didn't work. Why? The answer is that the parameter f is a
|>>double, not a float since all floats are converted to double when they are
|>>passed as arguments to functions. Therefore &f is a ptr to double
rather than a
|>>ptr to float as one would expect looking at the declartion.
|>
|>Sounds like a compiler bug to me. Floats are promoted to double in a
procedure
|>call but since your procedure declared the parameter as a float the compiler
|>should have "demoted" the parameter so that &variable would work properly.

Doesn't sound like a compiler bug to me. The compiler should have
"demoted" the parameter?!?!?! Huh?!?!?!? Is this some kind of
crazy VAXism????? Where's it being demoted? By the caller? How would it
know that it needs to demote it without a new style prototype in scope?
By the callee, then?? How does it know what's being passed? 
There's no such thing as demoting parameters. 

|>
|>FYI: Your code fragment worked properly with the VAX VMS-C compiler.

Could be luck, based on compatible representations of float and
double. But suppose on his machine, doubles have 2 more bits in
the exponent? Passing a double to a function that expects a 
float will work about as well as passing an integer to a function that
expects a float. 

|>
|>---
|>Ray Wallace		
|>		(INTERNET,UUCP) wallace@oldtmr.enet.dec.com
|>		(UUCP)		...!decwrl!oldtmr.enet!wallace
|>		(INTERNET)	wallace%oldtmr.enet@decwrl.dec.com
|>---
                       
==================
Chris Volpe
G.E. Corporate R&D
volpecr@crd.ge.com

browns@iccgcc.decnet.ab.com (Stan Brown, Oak Road Systems) (08/31/90)

In article <2117@krafla.rhi.hi.is>, pierre@rhi.hi.is (Kjartan Pierre Emilsson) writes:
> From article <0093BF08.7F3834E0@rigel.efd.lth.se>, by e89hse@rigel.efd.lth.se:
>> 
>> [Code deleted] 
>>	prnval(s,f)
>>	char *s;
>>	float f;
>> 
>> And it didn't work. Why? The answer is that the parameter f is a
>> double, not a float since all floats are converted to double when they are
>> passed as arguments to functions.Therefore &f is a ptr to double rather than
>> a float.
>> 
>>  Henrik Sandell
> 
> I don't know what other think, but as a relatively experienced C programmer, I
> find this bug very counter-intuitive, [...]

This isn't a bug--or at least it's not a compiler bug; it's the way the
language is designed to work.  Page 184, sec 6.2 of K&R 1: "whenever a float
appears in an expression it is lengthened to double. ..."  

But I think the real moral of the story is, always use prototypes.  The code
shown above doesn't allow the compiler to do type checking.  If the function
were defined as
	int prnval(char *s, float f)
	{
	    ...
then the compiler is not just able but _required_ to type-check the arguments.
(I think the original post had the function in the same source file and
preceding the code of the function that called it.  If that's not the case,
then the prototype declaration would be need to be in scope.)

Stan Brown, Oak Road Systems, Cleveland, Ohio, U.S.A.         (216) 371-0043
The opinions expressed are mine. Mine alone!  Nobody else is responsible for
them or even endorses them--except my cat Dexter, and he signed the power of
attorney only under my threat to cut off his Cat Chow!

volpe@underdog.crd.ge.com (Christopher R Volpe) (08/31/90)

In article <11477@crdgw1.crd.ge.com>, volpe@underdog.crd.ge.com
(Christopher R Volpe) forgets to do his homework:

Bart Schaefer corrected me on this one. I retract my last post.

I read the line sec A7.3.2 where it said "The effect of the call is undefined
if the ... type of an argument after promotion disagrees with that
of the corresponding parameter." (in K&R II). But I didn't realize
that they meant *promoted* type of the formal parameter. I should
have read the rest of the paragraph.

                                               
==================
Chris Volpe
G.E. Corporate R&D
volpecr@crd.ge.com

pejn@wolfen.cc.uow.oz (Paul Nulsen) (08/31/90)

wallace@ynotme.enet.dec.com (Ray Wallace) writes:

>In article <0093BF08.7F3834E0@rigel.efd.lth.se>, e89hse@rigel.efd.lth.se
>writes... 
>>... Therefore &f is a ptr to double rather than a
>>ptr to float as one would expect looking at the declartion.

>Sounds like a compiler bug to me.

I am not sure what the standard says, but this is definitely not a compiler
bug. With old K&R style declarations the compiler does not know the type of
a function argument until it sees the function definition. The function can
be invoked before the definition is known, and hence before the argument
types are known. In the case of floating point numbers, to avoid the
problems this creates the approach adopted was to promote all floating point
types to the common type of double. With ANSI style declarations this is no
longer necessary.

Compilers that accept ANSI style declarations have to accept the old style
as well for backward compatibility, and if they are going to do this
properly then they need to treat old style function definitions in the
manner that they would have been treated, i.e. they have to promote all
floats to doubles. This is what most compilers do. You will find that if you
use ANSI style declarations the problem does not arise.

I am surprised at the claim that VAX C does not promote the float to a
double, for the reasons I have just outlined. Did you use the old style
function definition in your test?

As someone who does a lot of number crunching, I can tell you that the
handling of floats in C is an significant obstacle to its use for heavy
floating point computations. Argument promotion is one of a number of
related gotcha's that make it difficult to write efficient single precision
floating point code. If you just need a few numbers, doubles are fine, but
when you are handling large data sets, the space and speed penalties can be
serious. ANSI declarations solve the problem of automatic promotion, but
there are of others. For example, the fact that floating constants are
doubles combined with the rules for type promotion in expressions means that
the following code
	float a, r;
	a = 3.1415926535 * r * r;
results in the promotion of r (at least once) and then the demotion of the
result back to float. To avoid this kind of trouble every constant has to be
cast:
	a = (float) 3.1415926535 * r * r;
which is a real pain.

If you aren't pressed for time and space always use double's - you will
probably end up using them whether you know it or not. If you are pressed
for space or time and have to use float's, then pity you.

Paul Nulsen
pejn@wolfen.cc.uow.edu.au

asrap@warwick.ac.uk (Sean Legassick) (08/31/90)

In article <11473@crdgw1.crd.ge.com> volpe@underdog.crd.ge.com (Christopher R Volpe) writes:
=In article <0093BF08.7F3834E0@rigel.efd.lth.se>, e89hse@rigel.efd.lth.se
=writes:
=|>Later I rewrote
=|>prnval() as:
=|>
=|>prnval(s,f)
=|>char *s;
=|>double f;
=|>{
=|>   if(f == 0.0)
=|>       sscanf(s,"%lf",&f);
=|>   printf("%10.2f\n",f);
=|>}
=
=This is the correct solution anyway.

	There is still a possible problem though. Comparing floating-point
values to 0 like that can cause problems, especially if the values been
put through the mangle of promotion etc. Its far less likely to cause problems
if you use something like :
	if (f < 0.005)

	As the orginal poster did not describe the nature of the problem when
the program was run (or compiled) this could have been the cause.
=|>
=|> Henrik Sandell
=                                          
===================
=Chris Volpe
=G.E. Corporate R&D
=volpecr@crd.ge.com

---------------------------------------------------------------------------
Sean Legassick,       cuuee@uk.ac.warwick.cu  "Improbability factor of one
Computing Services    asrap@uk.ac.warwick.cu    to one. We have normality.
University of Warwick      	 	         Anything you still can't
            (the walking C obfuscator!)	 	  handle is your own problem."

scjones@thor.UUCP (Larry Jones) (08/31/90)

In article <0093BF08.7F3834E0@rigel.efd.lth.se>, e89hse@rigel.efd.lth.se writes:
> 
> prnval(s,f)
> char   *s;
> float   f;
> {
>    if(f == 0.0)
>        sscanf(s,"%f",&f);
>    printf("%10.2f\n",f);
> }
> 
> And it didn't work. Why? The answer is that the parameter f is a
> double, not a float since all floats are converted to double when they are
> passed as arguments to functions. Therefore &f is a ptr to double rather than a
> ptr to float as one would expect looking at the declartion.

It is worth noting that different C compilers handle this situation
differently.  Some do exactly what you said, they rewrite your
parameter declaration as double rather than float.  Others assign
the passed double value to a local float which is then used as the
parameter.  If the float and double formats are sufficiently similar,
the compiler can just ignore the second half of the passed double
value and leave the parameter a float without having to make a copy.

ANSI C does not allow the declaration rewriting -- it requires the
compiler to make the parameter have the type you declared it as.
So, this problem should not be a problem much longer, right?
----
Larry Jones                         UUCP: uunet!sdrc!thor!scjones
SDRC                                      scjones@thor.UUCP
2000 Eastman Dr.                    BIX:  ltl
Milford, OH  45150-2789             AT&T: (513) 576-2070
Hello, I'm wondering if you sell kegs of dynamite. -- Calvin

meissner@osf.org (Michael Meissner) (08/31/90)

In article <6441@wolfen.cc.uow.oz> pejn@wolfen.cc.uow.oz (Paul Nulsen)
writes:

| wallace@ynotme.enet.dec.com (Ray Wallace) writes:
| 
| >In article <0093BF08.7F3834E0@rigel.efd.lth.se>, e89hse@rigel.efd.lth.se
| >writes... 
| >>... Therefore &f is a ptr to double rather than a
| >>ptr to float as one would expect looking at the declartion.
| 
| >Sounds like a compiler bug to me.
| 
| I am not sure what the standard says, but this is definitely not a compiler
| bug. With old K&R style declarations the compiler does not know the type of
| a function argument until it sees the function definition. The function can
| be invoked before the definition is known, and hence before the argument
| types are known. In the case of floating point numbers, to avoid the
| problems this creates the approach adopted was to promote all floating point
| types to the common type of double. With ANSI style declarations this is no
| longer necessary.

I would still argue it's a compiler bug, even in K&R-I.  If you said
float, what the compiler should do is realize that a double was
passed, take said double, convert it to float, and store it somewhere
(including back on the stack where it was passed).  Then any
references are still float, and not double.  You do have a conversion
from double to float in the procedure prologue.  The same conversion
goes for char and short parameters, though you can usually fake the
address (and using the little endian false belief, you don't have to
do that).  The one good thing you could say about IBM floating point
format, is that the float and double formats were the same, and you
could ignore the bottom bits.

ANSI requires the above behavior in the absence of prototypes.
--
Michael Meissner	email: meissner@osf.org		phone: 617-621-8861
Open Software Foundation, 11 Cambridge Center, Cambridge, MA, 02142

Do apple growers tell their kids money doesn't grow on bushes?

schaefer@ogicse.ogi.edu (Barton E. Schaefer) (08/31/90)

In article <MEISSNER.90Aug31103238@osf.osf.org> meissner@osf.org (Michael Meissner) writes:
} 
} In article <6441@wolfen.cc.uow.oz> pejn@wolfen.cc.uow.oz (Paul Nulsen)
} writes:
} 
} | wallace@ynotme.enet.dec.com (Ray Wallace) writes:
} | 
} | >In article <0093BF08.7F3834E0@rigel.efd.lth.se>, e89hse@rigel.efd.lth.se
} | >writes... 
} | >>... Therefore &f is a ptr to double rather than a
} | >>ptr to float as one would expect looking at the declartion.
} | 
} | >Sounds like a compiler bug to me.
} | 
} | I am not sure what the standard says, but this is definitely not a compiler
} | bug. With old K&R style declarations the compiler does not know the type of
} | a function argument until it sees the function definition. The function can
} | be invoked before the definition is known, and hence before the argument
} | types are known. In the case of floating point numbers, to avoid the
} | problems this creates the approach adopted was to promote all floating point
} | types to the common type of double. With ANSI style declarations this is no
} | longer necessary.

ANSI has nothing to do with it.  Not knowing the type before invocation
also has nothing to do with it.  The only reason that floats are widened
to double in a function call is because they are widened to double in
all expression contexts.  Similarly with char or short to int.

} I would still argue it's a compiler bug, even in K&R-I.  If you said
} float, what the compiler should do is realize that a double was
} passed, take said double, convert it to float, and store it somewhere
} (including back on the stack where it was passed).  Then any
} references are still float, and not double.  You do have a conversion
} from double to float in the procedure prologue.

To extend Michael's reasoning a bit:

    1.  The compiler (or more precisely, the compiler writer) knows
	that all floats will be promoted to double in expressions,
	including function calls.

    2.  It is legal to declare parameters to have type float. [%]

    3.  It is legal to take the address of a parameter. [%%]

    4.  From (2) and (3) it follows that taking the address of a
	parameter declared to be of type float must result in a
	pointer to type float.

    5.  From (4), it follows that the compiler must arrange that
	parameters declared to have type float will actually be
	represented as float (not double) when used in any function
	that takes the address of the float parameter.

    6.  From (2) and (1) it follows that the compiler (writer) knows
	that if the function was called with parameters of the correct
	types, any parameter declared float will have been promoted
	to double.  Therefore all the information needed to handle (5)
	is available, and some implicit "demotion" must occur if the
	address of a float parameter is taken.

How the compiler goes about dealing with (5) is not important -- it can
treat float as synonymous to double as the standard practice, but it must
special-case the unary & operator to be sure that it really does produce
a pointer to a float, not a pointer to a double masquerading as a float.
__________

%  Someone questioned this legality on the basis of a passage in K&R1,
   page 42, which notes that throughout the text K&R used int and double
   for parameters, rather than char or float, because of promotions.
   There are no passages (unless you count the grammar) that explicitly
   permit parameters of type char/short/float.  However, there also are
   no passages that explicitly forbid it, and it would have been trivial
   to prohibit it syntactically if that had been the intent.  I therefore
   contend that page 42 is explaining a choice of programming style, not
   a language restriction.

%% Of course you can't take the address of parameters declared register.
   Fortunately, that isn't at issue here.

-- 
Bart Schaefer						schaefer@cse.ogi.edu

chris@mimsy.umd.edu (Chris Torek) (09/02/90)

In article <7330028@hpcllca.HP.COM> walter@hpcllca.HP.COM (Walter Murray)
writes:
>You got bit by what is called type rewriting [in which a C compiler
changes the type of a formal parameter declaration to match the type
of any actual parameter passed by any caller, i.e., the compiler applies
type-widening rules].

>Type rewriting is not permitted for a compiler that conforms to the
>ANSI standard. ...

... except in the case of formal array parameters, where it is required:

	f(x) int x[]; { ... /* sizeof(x) == sizeof(int *) */

is the same as

	f(x) int *x; { ... /* sizeof(x) == sizeof(int *) */

and in fact the compiler is Officially Obliged to forget% that x was
declared with empty square brackets rather than an asterisk.  The
situation is unchanged when prototype definitions are used.

-----
% Well, modulo things not covered by the standard, such as program
  listings, error messages, and warnings.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

richard@iesd.auc.dk (Richard Flamsholt S0rensen) (09/03/90)

In article <11842@ogicse.ogi.edu> schaefer@ogicse.ogi.edu (Barton E. Schaefer) writes:
>} | >In article <0093BF08.7F3834E0@rigel.efd.lth.se>, e89hse@rigel.efd.lth.se
>} | >writes... 
>} | >>... Therefore &f is a ptr to double rather than a
>} | >>ptr to float as one would expect looking at the declartion.

>ANSI has nothing to do with it.  Not knowing the type before invocation
>also has nothing to do with it.  The only reason that floats are widened
>to double in a function call is because they are widened to double in
>all expression contexts.  Similarly with char or short to int.
 ^^^^^^^^^^^^^^^^^^^^^^^

  Nope. ANSI states, that floats remain floats unless the expression
contains more precise types, that is long double or double, in which
case the float in converted. Therefore, if the float is the only
parameter to a function it is *not* converted to a double in the
expression, regardless of how the function was declared.

  Nothing has changed to the treatment of chars and shorts. These (and
enums and bitfields) are still subject to integral promotion when
found in an expression.

  Hmmm <thinking> - I might as well quote from app. A in K&R II, to
make it totally clear:

\begin{illegitimate quote ;-}

  First, if either operand is a long double, the other is converted to
long double.
Otherwise, if either operand is a double, the other is converted to double.
Otherwise, if either operand is a float, the other is converted to float.
Otherwise, the integral promotion is performed on both operands;
[lotsa stuff about long/signed/unsigned/implementation dependencies deleted]

\end{quote}

  As K&R sum it up: "The 'usual arithmetic conversions' has changed,
essentially from 'for integers, unsigned always wins; for floating
point, always use double' to 'promote to the smallest capacious-enough
type'".

  CU.

--
/Richard Flamsholt
richard@iesd.auc.dk

scjones@thor.UUCP (Larry Jones) (09/04/90)

In article <RICHARD.90Sep3115406@orange.iesd.auc.dk>, richard@iesd.auc.dk (Richard Flamsholt S0rensen) writes:
>   Nope. ANSI states, that floats remain floats unless the expression
> contains more precise types, that is long double or double, in which
> case the float in converted. Therefore, if the float is the only
> parameter to a function it is *not* converted to a double in the
> expression, regardless of how the function was declared.

No, you've confused the "Usual Arithmetic Conversions" with the
"Default Argument Promotions".  The Usual Arithmetic Conversions
are the rules that you state -- float stays float.  Note, however,
that the compiler is usually allowed to get better results than
you might expect, so the old promotion to double is still allowed.

When calling a function which does not have a prototype in scope,
however, the Default Argument Promotions are applied to each of
the arguments.  These rules state that the normal Integral
Promotions are performed and that all floats are converted to
double, just like always.
----
Larry Jones                         UUCP: uunet!sdrc!thor!scjones
SDRC                                      scjones@thor.UUCP
2000 Eastman Dr.                    BIX:  ltl
Milford, OH  45150-2789             AT&T: (513) 576-2070
When you're SERIOUS about having fun, it's not much fun at all! -- Calvin

steve@taumet.com (Stephen Clamage) (09/04/90)

richard@iesd.auc.dk (Richard Flamsholt S0rensen) writes:

>  Nope. ANSI states, that floats remain floats unless the expression
>contains more precise types, that is long double or double, in which
>case the float in converted. Therefore, if the float is the only
>parameter to a function it is *not* converted to a double in the
>expression, regardless of how the function was declared.

This is just wrong.  Section 3.2.1.5 describes the "usual arithmetic
conversions", which are applied to binary operators.  The standard
allows a float to remain a float unless the other operand of the binary
operator is double or long double.  The last sentence of that section
says:
	"The values of floating operands and of the results of floating
	expressions may be represented in greater precision and range
	than that required by the type; the types are not changed thereby."
Thus ANSI allows floats to remain floats in expressions, but also allows
them to be promoted to wider types.

Furthermore, for function arguments, section 3.3.2.2 says:
	"If the expression that denotes the called function has a type
	that does not include a prototype, the integral promotions are
	performed on on each argument and arguments that have type
	float are promoted to double."
It goes on to say:
	"If the expression that denotes the called function has a type
	that includes a prototype, the arguments are implicitly
	converted, as if by assignment, to the types of the
	corresponding parameters."

So given the declarations:
	foo(float);
	doo(double);
	goo();
	float f;
We have the following calls:
	foo(f);	/* f passed as a float * /
	doo(f);	/* f widened to a double */
	goo(f);	/* f widened to a double */
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

chris@mimsy.umd.edu (Chris Torek) (09/04/90)

In article <RICHARD.90Sep3115406@orange.iesd.auc.dk> richard@iesd.auc.dk
(Richard Flamsholt S0rensen) writes:
>  Nope. ANSI states, that floats remain floats unless the expression
>contains more precise types, that is long double or double, in which
>case the float in converted.

This is *almost* right.  The ANSI C standard does not say that a
compiler cannot do `float f = 0.1; f += f;' as `convert f to double;
add double to double; convert result to float', but it does give
the compiler the option of doing it as `add float to float'.

(This is similar to the fact that the standard does not require a
compiler to, say, not insert delay loops after every few instructions
---except that the delay loops would be better proof of a stupid
compiler!  In other words, the standard does not, and cannot, outlaw
bad code generation.)

>Therefore, if the float is the only parameter to a function it is
>*not* converted to a double in the expression, regardless of how the
>function was declared.

This, however, is almost completely wrong.

When an actual parameter has type float, the value is not altered
*only* when a function prototype is in scope and the corresponding
formal parameter also has type float.  If no prototype is in scope,
or if the prototype has run into a `...', the value is widened.

This is easily demonstrated by noting that

	float f; double d; ...
	(void) printf("%f %f\n", f, d);

is correct: we do not need `%lf' to print d.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

richard@iesd.auc.dk (Richard Flamsholt S0rensen) (09/13/90)

In article <26350@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes:
> In article <RICHARD.90Sep3115406@orange.iesd.auc.dk> richard@iesd.auc.dk
> (Richard Flamsholt S0rensen) <that's me> writes:
> >  Nope. ANSI states, that floats remain floats unless the expression
> >contains more precise types, that is long double or double, in which
> >case the float in converted.
>
> This is *almost* right.  The ANSI C standard does not say that a
> compiler cannot do `float f = 0.1; f += f;' as `convert f to double;
> add double to double; convert result to float', but it does give
> the compiler the option of doing it as `add float to float'.

  Ooops - you're right: I'm wrong. Sure, optimizing is always legal.

> >Therefore, if the float is the only parameter to a function it is
> >*not* converted to a double in the expression, regardless of how the
> >function was declared.
>
> This, however, is almost completely wrong.

  No, but I should have emphazied my point. The original poster
claimed, that the float was converted, because it was part of an
expression and, as he said, "-in expressions, floats are always
converted to doubles". My objection to this was, that it isn't *the
expression* which converts the float - it's the fact, that said
expression is an argument to a function, which might convert it.

  Unfortunately, that point were lost in the function-business ...


--
/Richard Flamsholt
richard@iesd.auc.dk