I saw these two parameters in a C example in a C book but the author didn't elaborate what the difference between the two are. I know that %f specifies that a float should take its place. I tried looking this up but had a hard time trying to find this w symbols. What about %lf?
The short answer is that it has no impact on printf, and denotes use of float or double in scanf.
For printf, arguments of type float are promoted to double so both %f and %lf are used for double. For scanf, you should use %f for float and %lf for double.
More detail for the language lawyers among us below:
There is no difference between %f and %lf in the printf family. The ISO C standard (all references within are from C11), section 7.21.6.1 The fprintf function, paragraph /7 states, for the l modifier (my emphasis):
Specifies that a following d, i, o, u, x, or X conversion specifier applies to a long int or unsigned long int argument; that a following n conversion specifier applies to a pointer to a long int argument; that a following c conversion specifier applies to a wint_t argument; that a following s conversion specifier applies to a pointer to a wchar_t argument; or has no effect on a following a, A, e, E, f, F, g, or G conversion specifier.
The reason it doesn't need to modify the f specifier is because that specifier already denotes a double, from paragraph /8 of that same section where it lists the type for the %f specifier:
A double argument representing a floating-point number is converted to decimal notation
That has to do with the fact that arguments following the ellipse in the function prototype are subject to default argument promotions as per section 6.5.2.2 Function calls, paragraph /7:
The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.
Since printf (and the entire family of printf-like functions) is declared as int printf(const char * restrict format, ...); with the ellipsis notation, that rule applies here. The default argument promotions are covered in section 6.5.2.2 Function calls, paragraph /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.
For the scanf family, it mandates the use of a double rather than a float. Section 7.21.6.2 The fscanf function /11:
Specifies that a following d, i, o, u, x, X, or n conversion specifier applies to an argument with type pointer to long int or unsigned long int; that a following a, A, e, E, f, F, g, or G conversion specifier applies to an argument with type pointer to double; or that a following c, s, or [ conversion specifier applies to an argument with type pointer to wchar_t.
This modifies the /12 paragraph of that section that states, for %f:
Matches an optionally signed floating-point number, infinity, or NaN, whose format is the same as expected for the subject sequence of the strtod function. The corresponding argument shall be a pointer to floating.
For scanf, %f reads into a float, and %lf reads into a double.
For printf: In C99 and later, they both are identical, and they print either a float or a double. In C89, %lf caused undefined behaviour although it was a common extension to treat it as %f.
The reason that one specifier can be used for two different types in printf is because of the default argument promotions; arguments of type float are promoted to double when used to call a function and not matching a parameter in a function prototype. So printf just sees a double in either case.
The width modifier in %lf is gracefully ignored by printf(). Or, to be more accurate, %f takes a double - varargs will always promote float arguments to double.
For output using the printf family of functions, the %f and %lf specifiers mean the same thing; the l is ignored. Both require a corresponding argument of type double — but an argument of type float is promoted to double, which is why there’s no separate specifier for type float. (This promotion applies only to variadic functions like printf and to functions declared without a prototype, not to function calls in general.) For type long double, the correct format specifier is %Lf.
For input using the scanf family of functions, the floating-point format specifiers are %f, %lf, and %Lf. These require pointers to objects of type float, double, and long double, respectively. (There’s no float-to-double promotion because the arguments are pointers. A float value can be promoted to double, but a float* pointer can’t be promoted to a double* because the pointer has to point to an actual float object.)
But be careful using the scanf functions with numeric input. There is no defined overflow checking, and if the input is outside the range of the type your program’s behavior is undefined. For safety, read input into a string and then use something like strtod to convert it to a numeric value. (See the documentation to find out how to detect errors.)
Related
In variadic functions, default argument promotions occur.
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. [...]
6.5.2.2.7 [...] The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.
Therefore,
signed char c = 123;
int i = 123;
float f = 123;
double d = 123;
printf("%d\n", i); // ok
printf("%d\n", c); // ok, even though %d expects int.
printf("%f\n", d); // ok
printf("%f\n", f); // ok, even though %f expects double.
So why is there a printf length modifier for char (hh) and short (h)?
Section number refer to N2176.
Consider this example:
#include <stdio.h>
int main(void)
{
unsigned short x = 32770;
printf("%d\n", x) ; // (1)
printf("%u\n", x) ; // (2)
}
On a typical 16-bit implementation, the default argument promotions take unsigned short to unsigned int, whereas on a typical 32-bit implementation, unsigned short becomes int.
So on the 16-bit system (1) is UB and (2) is correct, but on the 32-bit system, (1) is correct and (2) it can be debated whether correct or UB.
Using %hu for printing x works on all systems and you don't have to think about these issues.
A similar example could be constructed for char on systems with sizeof(int) == 1.
It's for backwards compatibility.
In a draft version of the C89 standard, printing a signed int, short or char with the %xformat specifier is not undefined behavior:
d, i, o, u, x, X The int argument is converted to signed decimal ( d or i ), unsigned octal ( o ), unsigned decimal ( u ), or unsigned hexadecimal notation ( x or X ); the letters abcdef are used for x conversion and the letters ABCDEF for X conversion. The precision specifies the minimum number of digits to appear; if the value being converted can be represented in fewer digits, it will be expanded with leading zeros. The default precision is 1. The result of converting a zero value with an explicit precision of zero is no characters.
This seems to document that pre-standardized C, using format specifiers such as %x for signed values was an existing practice, thus a preexisting code base using h and hh length modifiers likely existed.
Without the h and hh length modifiers, signed char values with a bit pattern 0xFF would be printed on a 32-bit-int system as 0xFFFFFFFF if printed with a simple %X format specifier.
Regarding the hh specifier specifically, it was explicitly added in C99 in order to utilize printing of all the default fixed size types from stdint.h/inttypes.h. C99 mades the types int_leastn_t from 8 to 64, mandatory, so there was a need for corresponding format specifiers.
From the C99 rationale 5.10, §7.19.6.1 (fprintf):
The %hh and %ll length modifiers were added in C99 (see §7.19.6.2).
§7.19.6.2 (fscanf):
A new feature of C99: The hh and ll length modifiers were added in C99. ll supports the new long long int type. hh adds the ability to treat character types the same as all other integer types; this can be useful in implementing macros such as SCNd8 in <inttypes.h> (see 7.18).
Before C99, there was just d, h and l for printing integer types. In C99, a conventional implementation could for example define inttypes.h specifiers as:
#define SCNi8 hh
#define SCNi16 h
#define SCNi32 d
#define SCNi64 ll
And now the default argument promotions becomes the head ache of the printf/scanf implementation, rather than the inttypes.h implementation.
They are not there for printf() usage, but for scanf() to be able to use references to short integers and char integers. For uniformity and completeness, they are accepted for printf() functions, but they are undistinguisable, as the vaarg parameters of printf() are promoted to int for all parameters that are of types short and char integer values. So they are equivalent in printf() but not in scanf() and friends.
int main(){
int s=200;
printf("%.12f",s/3.0);
}
How this program is giving output 66.666666666667 even though float can support up to only 6-7 decimal places?
Meanwhile,
int main(){
float s=200;
printf("%.12f",s/3);
}
gives 66.666664123535.
please help?
There isn't any float in the first program. %f is double and 3.0 is also double. The double type has about 16 decimal digits precision. In the second program the float is promoted to double for the variadic function but does not magically gain more precision.
From C11 § 6.4.4.2
4 An unsuffixed floating constant has type double. If suffixed by the letter f or F, it has type float. If suffixed by the letter l or L, it has type long double.
and C11 § 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.
7 If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type. The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.
Only if the precision was missing, it would have been taken as 6. Otherwise, if you specify the precision, it's going to honor that.
Quoting C11, chapter 7.21.6.1/P8, (emphasis mine)
f,F
A double argument representing a floating-point number is converted to decimal notation in the style [-]ddd.ddd, where the number of digits after the decimal-point character is equal to the precision specification. If the precision is missing, it is taken as 6; [...]
In most C implementations, float and double do not use decimal digits. They use binary.
In the first program, 3.0 is a double, so s/3.0 is computed with double arithmetic. In spite of f being the initial letter of float, %.12f takes a double argument, for reasons involving the history of C development. So the double value s/3.0 is converted to decimal with 12 digits after the decimal point and printed.
In the second program, s is a float, so s/3 is computed with float arithmetic. In the format you use, the result is a binary-based number, 8738133 • 2−17. The reason there are many decimal digits involved is that, while expressing 2−17 is simple using that base of 2, in decimal it is 0.00000762939453125. then 8738133 times that is 66.66666412353515625. Since you asked for only 12 digits, you see 66.6666641235. (Note: The float value is converted to double to be passed to printf, but this does not change the value.)
For example, I use printf function in C++ for 8-bit CPU (AVR). Is the following code safe:
uint8_t a = 5;
printf("%d", a);
Here %d expects int (16-bit in my case, and at least 16-bit in any case), but I pass 8-bit integer.
Does C/C++ standards guarantee that any type with rank lesser than int promoted to int?
The same question for float a and %f that expects double, and other analogous types.
Look in the draft n1256 (C99 with Technical corrigenda TC1, TC2, and TC3 included) for 6.5.2.2 Function calls:
For functions without prototype, or parameters corresponding to the ellipsis ..., the default argument promotions are performed.
Those are: Default integer promotions and promotion of float to double.
Default integer promotions: Every integer type of rank less than int is promoted to int or unsigned int.
Here's what libc has to say about variadic functions:
Since the prototype doesn’t specify types for optional arguments, in a call to a variadic function the default argument promotions are performed on the optional argument values. This means the objects of type char or short int (whether signed or not) are promoted to either int or unsigned int, as appropriate; and that objects of type float are promoted to type double. So, if the caller passes a char as an optional argument, it is promoted to an int
Then, why would anyone use "%c", or "%hd" in printf ? they should just use "%d".
I also see that there's no format specifier for float. float has to live with %f which is for double since due to promotions, it's not possible to receive a float as a variadic argument.
I know for scanf, the arguments are pointers and no promotion happens.
Is there any reason I am missing why and when "%c" must exist for printfs?
Then, why would anyone use "%c", or "%hd" in printf ? they should just use "%d".
One would use %c to interpret the integer as its character code (i.e. print 'A' instead of 65). One would use %hd to instruct printf to drop the upper portion of the short that may have been added as part of sign-extending the short value passed in. Both formats offer an alternative interpretation of an int.
I also see that there's no format specifier for float.
That's correct: since the value has been promoted to double, there is no need for a separate flag.
scanf needs %lf for doubles and printf is okay with just %f
So, why is printf and scanf okay with %d?
This is what I think the reason is:
A floating point (%f) uses exactly 64 bits whereas a double floating-point number (%lf) uses at least 32. The compiler doesn't know how many bits to assign to a variable that is being scanned in by scanf, so we use %lf to tell the compiler that it needs to be at least 32 bits.
Okay... but then why do we use %d for both scanf and printf? Why not %ld and %d? %ld doesn't exist in C for starters. %d is a signed decimal number that needs at least 16 bits. You're already telling the compiler what the lower bound is in how many bits to allocate to it, so it is okay for scanf. This is also why we don't have a %ld.
Please do correct me if I am wrong or inform me what I can do to make this answer better. I'm pretty sure it is not a perfect answer.
You can think scanf as converting input stream into variables that defined in your code. Thus, scanf needs to know the exactly size for each variable. In general, the 32-bit and 64-bit IEEE 754 binary floating-point formats are used in C. So, %f means 32-bit and %lf means 64-bit.
Besides %ld exists and it means 32-bit integer. %lld also exists which means 64-bit integer. The above wiki site explains all C data types very well.
See §6.5.2.2/6-7 in the C99 standard.
§6.5.2.2/6 defines the default argument promotions: (emphasis added)
the integer promotions are performed on each argument, and arguments that have type float are promoted to double.
and specifies that these promotions are performed on arguments to a function declared with no prototype (that is, with an empty parameter list () instead of (void), where the latter is used to indicate no arguments).
Paragraph 7 additionally specifies that if the prototype of the function has a trailing ellipsis (...):
The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.
The integer promotions are defined in §6.3.1.1/2; they apply to
objects or expressions of an integer type whose "integer conversion rank is less than or equal to the rank of int and unsigned int": roughly speaking, any smaller integer type, such as boolean or character types;
bit-fields of type _Bool, int, signed int or unsigned int.
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned
int. These are called the integer promotions.
All other types are unchanged by the integer promotions.
In short, if you have varargs function, such as printf or scanf:
Integer arguments which are no larger than int are converted to (possibly unsigned) int. (This does not include long.)
Floating point arguments which are no larger than double are converted to double. (This includes float.)
Pointers are unaltered.
Other non-pointer types are unaltered.
So printf doesn't need to distinguish between float and double, because it will never be passed a float. It does need to distinguish between int and long.
But scanf does need to know whether an argument is a pointer to a float or a pointer to a double, because pointers are unchanged by the default argument promotions.