Why does printf() promote a float to a double?
Yes, float arguments to variadic function are promoted to double.
The draft C99 standard section 6.5.2.2
Function calls says:
[...]and arguments that
have type float are promoted to double. These are called the default argument
promotions.[...]
from the draft C++ standard section 5.2.2
Function call:
[...]a floating point type that is subject to the floating point
promotion (4.6), the value of the argument is converted to the
promoted type before the call. [...]
and section 4.6
:
A prvalue of type float can be converted to a prvalue of type double. The value is unchanged
cppreference covers the default conversions for variadic function in C++ well:
- std::nullptr_t is converted to void*
- float arguments are converted to double as in floating-point promotion
- bool, char, short, and unscoped enumerations are converted to int or wider integer types as in integer promotion
We can see in C and presumably in C++ this conversion was kept around for compatibility with K&R C, from Rationale for International Standard—Programming Languages—C (emphasis mine):
For compatibility with past practice, all argument promotions occur as
described in K&R in the absence of a prototype declaration, including
the not always desirable promotion of float to double.
Why when using printf float and double show the same number of digits?
sizeof
returns size_t
. To print size_t
you need %zu
instead of %d
If you want to see the real difference between float
and double
you need to print more digits using %.NUMBERf
Like:
#include <stdio.h>
int main(void)
{
float x=1.2222222222222222f;
printf("%.70f %zu\n", x,sizeof(x));
double y=1.2222222222222222;
printf("%.70f %zu\n", y,sizeof(y));
return 0;
}
Output:
1.2222222089767456054687500000000000000000000000000000000000000000000000 4
1.2222222222222220988641083749826066195964813232421875000000000000000000 8
Is there a printf specifier that requires float not double?
No, because printf
and its friends are variadic functions, so a float
parameter undergoes automatic conversion to double
as part of the default argument promotions (see section 6.5.2.2 of the C99 standard).
I'm not sure why this triggers a MISRA warning though, I can't think of any way in which this might be dangerous.
How does printf and co differentiate between float and double
It doesn't differentiate. It's not possible to receive a float
as a vararg: any float
argument that you provide is first promoted to double
.
6.5.2.2/6 defines "default argument promotions", and /7 states that default argument promotions are applied to "trailing arguments", that is varargs denoted by ...
.
how does it work for scanfs/sscanf?
The %f
format for scanf
requires a pointer to float
. %lf
requires a pointer to double
, %Lf
requires a pointer to long double
.
copying the value to a temp and casting(is this right?)
If you provide a float argument, then the implementation creates a temporary of type double, initializes it with the float value, and passes this as the vararg. Casting by definition is explicit conversion by use of the cast operator -- you can cast if you like in order to make it exactly clear to the reader what's going on, but float f = 3; printf("%f", f);
is exactly the same as float f = 3; printf("%f", (double)f);
. The default argument promotion has the same meaning as the cast.
Why does scanf() need %lf for doubles, when printf() is okay with just %f ?
Because C will promote floats to doubles for functions that take variable arguments. Pointers aren't promoted to anything, so you should be using %lf
, %lg
or %le
(or %la
in C99) to read in doubles.
Correct format specifier for double in printf
"%f"
is the (or at least one) correct format for a double. There is no format for a float
, because if you attempt to pass a float
to printf
, it'll be promoted to double
before printf
receives it1. "%lf"
is also acceptable under the current standard -- the l
is specified as having no effect if followed by the f
conversion specifier (among others).
Note that this is one place that printf
format strings differ substantially from scanf
(and fscanf
, etc.) format strings. For output, you're passing a value, which will be promoted from float
to double
when passed as a variadic parameter. For input you're passing a pointer, which is not promoted, so you have to tell scanf
whether you want to read a float
or a double
, so for scanf
, %f
means you want to read a float
and %lf
means you want to read a double
(and, for what it's worth, for a long double
, you use %Lf
for either printf
or scanf
).
1. C99, §6.5.2.2/6: "If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions." In C++ the wording is somewhat different (e.g., it doesn't use the word "prototype") but the effect is the same: all the variadic parameters undergo default promotions before they're received by the function.
Does float always auto-convert to double when multiplying mixed data types?
It must refer to the result of binary arithmetic operations of float * float
format. In the pre-standard versions of C operands of such expressions were promoted to double
and the result had double
type.
For example, here's a quote from "C Reference Manual"
If both operands are int or char, the result is int. If both are
float or double, the result is double.
In C89/90 already this behavior was changed and float * float
expressions produce float
result.
- If either operand has type long double, the other operand is converted to long double
- Otherwise, if either operand is double, the other operand is converted to double.
- Otherwise, if either operand is float, the other operand is converted to float.
Related Topics
C/C++ Check If One Bit Is Set In, I.E. Int Variable
What Are the Pointer-To-Member Operators -≫* and .* in C++
Why Can't I Have a Non-Integral Static Const Member in a Class
What Is More Efficient? Using Pow to Square or Just Multiply It With Itself
Initializing Fields in Constructor - Initializer List VS Constructor Body
How to Construct a Std::String With an Embedded Null
How to Get the Ip Address of a Local Computer
How to Get Error Message When Ifstream Open Fails
Why Is the Destructor of a Future Returned from 'Std::Async' Blocking
How to Print Utf-8 from C++ Console Application on Windows
How to Detect Win32 Process Creation/Termination in C++
Initialize a Const Array in a Class Initializer in C++
Why Is C++11'S Pod "Standard Layout" Definition the Way It Is
Why Doesn't a Derived Template Class Have Access to a Base Template Class' Identifiers