What's the correct way to sprintf a float? - c

I'm trying to printf/sprintf floats using this code:
sprintf(buff, "FPS: %d\n%.4f N %.4f E\nAl: %.1fm Rl: %.1f\n", fps, p_viewer.p.lat, p_viewer.p.lon, p_viewer.p.alt, p_viewer.roll);
However I'm getting these warnings when I do so:
gfx_game_engine.c:300: warning: format '%.4f' expects type 'double', but argument 4 has type 'float'
gfx_game_engine.c:300: warning: format '%.4f' expects type 'double', but argument 5 has type 'float'
gfx_game_engine.c:300: warning: format '%.1f' expects type 'double', but argument 6 has type 'float'
gfx_game_engine.c:300: warning: format '%.1f' expects type 'double', but argument 7 has type 'float'
What's the correct way to sprintf a float? Is there a special format character? I feel that the compiler might be casting the types somehow and this might contribute to it slowing down.
I'm using a dsPIC33FJ128GP802 microcontroller and compiling with MPLAB C30, a variant of GCC v3.23.

Your variant of gcc is broken. In the C language, there is no way to pass a float to a variadic function. Default promotions of small types (char, short, and float) up to at least int/double always apply. I'm guessing whoever hacked gcc for this microcontroller did something to disable promotions, probably with the idea that double is slow or hard to pass. You should flame them to hell and back. The correct solution, if a compiler vendor does not want to support double correctly, is to make all the floating point types the same size and equivalent to float, not to violate the promotion rules of the language.
Another idea: It's also possible that the prototype for sprintf is missing or wrong, for instance int sprintf(); instead of int sprintf(char *, const char *, ...);

float arguments should automatically be converted to double. There seems no other way to print a float. Your compiler shouldn't complain in this case.

Unlike for scanf, where %f means float and %lf means double, printf does not distinguish between them. So if printf (or its variants) are implemented via a library linkage, the compiler will have to convert.

Related

Are there better ways to ease this warning?

I have a struct where I use bitfields to optimize memory. I have a uint64_t type and I want to print its value. When compiled it shows me this warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 5 has type ‘long unsigned int:48’ [-Wformat=]
I have already tried to supress this warning by typing while compiling -Wno-format. I'm wondering if there are a better way to do it.
Here some code:
#include <stdint.h>
#include <stdio.h>
typedef struct gn_addr
{
unsigned int m:1;
unsigned int st:5;
unsigned int reserved:10;
uint64_t mid:48;
} gn_addr ;
void gn_addr__print(gn_addr *self)
{
printf("M=>%d\nST=>%d\nReserved=>%d\nMID=>%lu\nTotal size %ld bytes\n",
self->m, self->st, self->reserved, self->mid, sizeof(self));
}
While you should definitely apply the fixes in the other answers to get portable format specifiers, the warning will persist. The reason is that extra arguments to a variadic function like printf undergo argument promotions. Argument promotions include integer promotion.
The rules for integer promotions will convert any integer with a conversion rank less than int/unsigned, as well as bit-fields, into the an int/unsigned. So for your initial bit-fields, you get int automatically.
For integers with a higher conversion rank than int/unsigned, no promotion occurs. So your bit-field is not promoted to an uint64_t, and you get a warning about argument mismatch. You need a cast.
(uint64_t)self->mid
Btw, since nobody mentioned, the portable format specifier for a size_t (the type of the sizeof operator) is %zu. You should use that instead of %ld.
First, use proper format specifier for uint64_t and size_t types, use PRIu64 macro, as defined in inttypes.h and %zu.
That said, for the bit field variables,
you can either use a cast, like (uint64_t)self->mid
or you can use an intermediate variable of type uint64_t, assign the member variable value to the new intermediate variable, and pass that to printf() as the corresponding argument to the format specifier, like
uint64_t temp = self->mid;
printf("%"PRIu64 "\n", temp);
Functions like printf are being examined by compilers (not all). The compiler checks if format specifier matches with the type of argument passed. You need to make explicit conversion to make that Warning go away. As mentioned in other answers that there are some implicit integer promotion rules and you need to be aware of them (Thanks to #storyTeller).
The rules for integer promotions will convert any integer with a conversion rank less than int/unsigned,as well as bit-fields, into the an int/unsigned. So for your initial bit-fields, you get unsigned int automatically.
For integers with a higher conversion rank than int/unsigned, no promotion occurs. So your bit-field is not promoted to an uint64_t, and you get a warning about argument mismatch. You need a cast.
Try this:
(uint64_t)self->mid
I have already tried to supress this warning by typing while compiling -Wno-format. I'm wondering if there are a better way to do it.
It's not a good idea to suppress your warnings. They are generated for a reason. I would recommend to use warning flags like -Wall, -Wshadow, -Wextra and other important flags. They can help you with deploying code with lesser number of bugs.
Regarding your assumption:
I have a struct where I use bitfields to optimize memory.
I would like to point out that your struct with bitfields won't be that much memory optimized as you might be considering it. There is something called Padding and Data Alignment which may help you further in reducing the memory footprint.
Check this question.

Different behavior of printf() in Turbo C and gcc when trying to print a pointer

The code below gives the output without error in Turbo C compiler, and gives the address of variable and its value both:
int a=5,*fp;
fp=&a;
printf("%d %d\n",fp,*fp);
But when I compile the same code in Linux with GCC compiler, it gives an error:
`warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat=]
printf("%d %d\n",fp,*fp);`
But the same code with the %p format specifier works in GCC Compiler ,to which I agree. The question is: how come it's working on Turbo C platform?
P.S. The issue is not that (in Turbo C) the error is not reported but it's that on Turbo C
it gives a signed integer value that is unchanged on repeated execution of the program; can it be garbage?
P.P.S Turbo C is running on MSDOS platform and GCC on 64-bit Linux, if that helps.
printf() assumes you will pass a signed int for the %d specifier.
On the platform and compiler you are using, ints and pointers are probably the same size and printf() is able to display it correctly. Your pointer is reinterpreted as an int.
Compiling and running this on a different platform where, for example, ints are 32-bit and pointers are 64-bit (such as gcc on x64) would be undefined behaviour. If you are lucky enough, it would crash. If not, you'd get garbage.
The %d conversion specifier requires the corresponding argument to have type [signed] int. If the corresponding argument in fact has a different type then the behavior is explicitly undefined. This is the case regardless of the relative sizes of the expected and actual types, and regardless of whether implicit or explicit conversion between those types is possible.
Neither the behavior of the compiler nor that of any part of any resulting compiled program is defined when the program exhibits undefined behavior. Turbo C is not required to diagnose the problem. On the other hand, gcc is quite permitted to diagnose it, and even to reject the source code with an error instead of merely alerting you with a warning. As far as C is concerned, the whole program's behavior can be absolutely anything if any undefined behavior is triggered -- from what the author intended (whatever that may be) to emitting rude comments from the machine's speaker, and far beyond.
In practice, the undefined behavior is likely (but by no means certain) to manifest in a relatively benign way if the expected and actual types are the same size, and the conversion specifier is not %s. Otherwise, all bets are off. Note in particular that many C implementations for 64-bit platforms feature 32-bit ints and 64-bit pointers.
Each conversion specifier, such as %d, specifies both the type of the required argument and the format used to print it.
%d requires an argument of type int (equivalently signed int), and prints it in decimal.
%u requires an argument of type unsigned int and prints it in decimal.
%x requires an argument of type unsigned int and prints it in hexadecimal.
And so forth.
In your code:
int a=5,*fp;
fp=&a;
printf("%d %d\n",fp,*fp);
the second %d is correct, since the corresponding argument, *fp is of type int. But the first is incorrect, since the corresponding argument fp is of type int*.
If you pass an argument of the wrong type for a conversion specifier, the behavior is undefined. A compiler is not required to warn you if you do this, since it's not possible to detect the error in the most general case (the format string doesn't have to be a string literal). Some compilers, including gcc, will analyze format strings if they're string literals and warn about mismatches. Turbo C apparently does not (that's not surprising, it's a very old compiler).
The correct format for printing a pointer value is %p. This requires an argument of type void*, and prints it in an implementation-defined manner. A pointer of a type other than void* should be converted.
The correct version of your code is:
int a = 5, *fp;
fp = &a;
printf("%p %d\n", (void*)fp, *fp);
It's a warning, not an error. The first compiler could also say the same, but doesn't.
The warning doesn't stop the compilation, so it will work too. They're just different compilers. Also, compiler accepting your program doesn't mean that the program is correct.

C compiler warning error

A simple program
#include<stdio.h>
int main() {
char b='a';
printf("%s \n", b);
return 0;
}
Output:
test.c: In function ‘main’:
test.c:4:1: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
printf("%s \n", b);
^
But the second arg is char not int.
Is this a wrong warning by the compiler, or something else I am missing?
The compiler is certainly correct to warn about the call. printf with a "%s" specifier requires an argument of type char* (which must point to a string), and the argument you pass is not of that type.
As for why the warning message refers to int rather than char, it's because printf is a variadic function. The first parameter, the format string, is declared to be of type const char*, but the following arguments are specified only as , .... In this special case, arguments of integer types narrower than int are promoted to int or to unsigned int. So even though the expression b is of type char, the actual argument that's passed to printf is of type int; specifically, it's the result of converting the value of b from char to int.
The warning is accurate, though the reasons are modestly obscure.
The declaration of printf() is:
int printf(const char *restrict format, ...);
The const and restrict keywords aren't important for this discussion. What is important is the ellipsis, .... When arguments are passed to a function with a variable argument list (a variadic function), they undergo 'default argument conversions'. Integer types shorter than int (short and char in their various forms) are promoted to int, and float values are promoted to double. Thus, the value of b is converted to int by the rules for calling a variadic functions.
The "%s" format expects to be given a pointer to a null-terminated character string. The variable b is a single char, not a string. Consequently, the compiler is correctly warning you that you will not get good results from running the program.
In this context, using a format such as %c (to print a character) or %d (to print a decimal integer) is probably best:
printf("%c\n", b);
As a general rule, at this stage in your C programming career, you should assume the compiler is right and that you're wrong. Remember, the C compiler knows a lot more about C than you do. This isn't to say that there are never bugs in compilers. However, the chances of you finding one are slim. Until you know enough about C (maybe in five to ten years time), then you should assume the compiler is right, you're wrong, and work out (a) what the compiler means and (b) how to fix it.
It was not always thus. Thirty years ago, bad compilers existed because the best were not all that much better. It was possible to find bugs in those compilers. There has, however, been a serious winnowing and few incompetent compilers are left on the market. One area where you can sometimes find compilers with surprising limitations (occasionally tantamount to bugs) is in specialized embedded systems for obscure chips. However, in mainstream o/s for desktops and servers (and tablets and smart phones), you're unlikely to come across a seriously defective compiler.
ISO/IEC 9899:2011 §6.5.2.2 Function calls
¶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. If the number of arguments does not equal the number of parameters, the
behavior is undefined. If the function is defined with a type that includes a prototype, and
either the prototype ends with an ellipsis (, ...) or the types of the arguments after
promotion are not compatible with the types of the parameters, the behavior is undefined.
If the function is defined with a type that does not include a prototype, and the types of
the arguments after promotion are not compatible with those of the parameters after
promotion, the behavior is undefined, except for the following cases:
one promoted type is a signed integer type, the other promoted type is the
corresponding unsigned integer type, and the value is representable in both types;
both types are pointers to qualified or unqualified versions of a character type or
void.
The 'integer promotions' are defined in §6.3.1.8 Usual arithmetic conversions. They're more complex than I want to go through here.
a char is really just an 8 bit number, so a single char alone is no different than an int, except that it can only store smaller numbers. In your printf statement you have
%s
but a single char is not considered a string, just a number, so you should use
%c or %d
for you printf statement. If you had an array of chars, then you would use %s
Try this instead:
printf("%c \n", b);
The compiler is casting the char to int on the fly, so the error msg makes sense (at least to someone used to interpreting C compiler messages).

gcc scanf warning believes float is double

I have a question regarding a warning message from the gcc compiler. The warning message occurs when an argument of scanf isn't a pointer to the variable that should carry the user input.
#include <stdio.h>
int main(int argc, const char *argv[]) {
float number;
scanf("%f", number); /* the value of 'number' is passed, instead of the adress to it */
return 0;
}
gcc will give the following warning message when it compiles the program.
scanf-problem.c: In function 'main':
scanf-problem.c:5:5: warning: format '%f' expects argument of type 'float *', but argument 2 has type 'double' [-Wformat=]
scanf("%f", number);
^
Like expected gcc wants the second argument of scanf to have the type 'float *' (the pointer to a float). What troubles me is that gcc believes that the second argument have the type 'double', when it actually have the type 'float'.
Which leads me to the question, why do gcc believe the second argument of scanf to be a double, when it actually is a float?
I have made some research on this topic to get an answer, but every answer I find is about how to get rid of the warning (write '&number' instead of 'number').
Which leads me to the question, why do gcc believe the second argument
of scanf to be a double, when it actually is a float?
Because float is promoted to double as specified in C Standard
6.5.2.2 Function calls
[#6] ... arguments
that have type float are promoted to double. These are
called the default argument promotions.
[#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.
You already know that you are passing wrong thing (as mentioned in comment) to scanf So just informing you reason behind the warning is float argument that you provide is first promoted to double.
Your compiler is right, the argument passed to scanf is indeed a double: for the ... part of the argument list a set of default conversions is performed. In particular all float arguments are promoted to double and this is what scanf gets to see.
[Edit] Other than posting the scanf() prototype, I now see the answer echoes #chill.
"imitation is the sincerest form of flattery."
Check the prototype for scanf()
int scanf(const char * restrict format, ...);
That ... means any number of any type of arguments are allowed. For historic reasons, all FP arguments of smaller than double are promoted to double when passed to such functions. That why the compiler calls it a double.
char, short are promoted to int too.
C11 6.5.2.2 6
" ... integer promotions are performed on each argument, and arguments that
have type float are promoted to double. These are called the default argument
promotions. ... "
The error message was misleading, scanf expect an address to the variable to fill the data back to it and you passed a value, and when scanf lookup that address it finds it to be different than what was suggested in formating "%f"
you can change it to
scanf("%f", &number); // &number instead of number
and it will work just fine

printf for float behaves differently if position changed

I've below piece of code :
float i=85.00;
printf("%f %p",i,i);
which prints o/p:
85.00000 (nil)
But when I change the order like below:
float i=85.00;
printf("%p %f",i,i);
the o/p is :
(nil) 0.00000
While I expect that similar o/p should be printed as earlier, in mentioned order.
What is the behaviour going on can anyone please explain?
What you're doing is undefined behavior. The %p format specifier promises that you're passing in a pointer (specifically a void* pointer), and you're passing in a double instead (float values get promoted to double when passed to variadic functions such as printf). So as soon as you fail to satisfy the requirements, the compiler and implementation are free to do whatever they want, and printing out 0 instead of 85 in the second case is perfectly legal.
What's likely happening is that the compiler is using a calling convention which places floating-point values in separate registers (e.g. the x87 floating-point stack, or the SSE2 SIMD registers) instead of on the stack, whereas integer values like pointers get passed on the stack. So when the implementation sees a %p format specifier, it tries to read the argument off of the stack, when in fact the argument is actually elsewhere.
As others have mentioned, you should really be compiling with the -Wall option (assuming GCC or a GCC-compatible compiler). It will help you catch these kinds of errors.
printf() expects a pointer for the format specifier %p whereas you are passing the value of a float. This is undefined behaviour.
If you want to print the address, then pass the address:
printf("%f %p",i, &i);
If you turn on the warnings, you'll see the problem:
warning: format ‘%p’ expects argument of type ‘void *’, but argument 3
has type ‘double’ [-Wformat]
You are probably hitting some undefined behavior; printing a float as a pointer has absolutely no sense.
And GCC 4.8 invoked as gcc -Wall rightly warns you:
dwakar.c:5:3: warning: format '%p' expects argument of type 'void *',
but argument 3 has type 'double' [-Wformat=]
printf("%f %p",i,i);
^
So please use gcc -Wall to compile your code and correct it till no warnings are given.
Notice that at the ABI level floating point and pointers arguments are passed thru different registers. So the implementation probably prints as pointer the (uninitialized, or old) content of whatever register is involved.

Resources