Reputation: 69367
It happened to me several times to work with float
variables having to printf
them out properly formatted with a certain number of decimal positions. I know that in C you can easily format float
variables by using the following notation to specify both decimal and total digits:
// Where x and y are natural numbers
printf("%<x>.<y>f", myFloat);
And I also know that you can use the *
symbol to dynamically format a float
using two variables, like this:
// Where x and y are natural numbers
printf("%*.*f", x, y, myFloat);
But I recently got stuck in a really redundant situation: when I have to printf
out multiple float
numbers with the same dynamic format I have to repeat these two x
and y
variables thousands of times. I always like to #define
my constants so that I can easily change them in the future, so my programs look something like:
#define FLOAT_FORMAT_SIZE 10
#define FLOAT_FORMAT_PRECISION 3
int main(int argc, char *argv[]) {
// Anything I have to do...
// Output
printf("%*.*f, %*.*f, %*.*f, %*.*f",
FLOAT_FORMAT_SIZE, FLOAT_FORMAT_PRECISION, myFloat1,
FLOAT_FORMAT_SIZE, FLOAT_FORMAT_PRECISION, myFloat2,
FLOAT_FORMAT_SIZE, FLOAT_FORMAT_PRECISION, myFloat3,
FLOAT_FORMAT_SIZE, FLOAT_FORMAT_PRECISION, myFloat4
);
return 0;
}
Now you can see that this is a bit too redundant, and I was wondering: isn't there any special system constant or format technique I can use to avoid repeating the same values over and over again for every float
I printf
out?
I don't want to create my own function to print out these numbers, I can obviously do it, but I'd like to solve it on a lower level, if it's possible.
Upvotes: 0
Views: 1510
Reputation: 29126
You can write a macro that defines your default way of printing floats:
#define FLOAT_FORMAT_SIZE 10
#define FLOAT_FORMAT_PRECISION 3
#define M_STR(x) M_STR_(x)
#define M_STR_(x) #x
#define FLOAT_FMT \
"%" M_STR(FLOAT_FORMAT_SIZE) \
"." M_STR(FLOAT_FORMAT_PRECISION) \
"f"
Then your printf
statement will look like this:
printf(FLOAT_FMT ", " FLOAT_FMT ", " FLOAT_FMT ", " FLOAT_FMT,
myFloat1, myFloat2, myFloat3, myFloat4);
Both the macro and how it is used use string literal concatenation. The format string is created at compile time, so that warnings about format and argument type mismatch will be triggered.
It is, of course, debatable whether is format string is very readable.
Edit: How does this work? The macro M_STR
"stringifies" its argument; it turns it into a C string literal. The operator #
, which is available in macro definitions, achieves this.
The indirection to the auxiliary macro M_STR_
must be used, so that the macro argument will be expanded first. Without this auxiliary macro, M_STR(FLOAT_FORMAT_SIZE)
would be expanded to "FLOAT_FORMAT_SIZE"
. With the detour via M_STR_
, the macro FLOAT_FORMAT_SIZE
is expanded before it is turned into a string and the resulting string is "10"
.
Hence, the macro FLOAT_FMT
expands to
"%" "10" "." "3" "f"
The C compiler concatenates adjacent string literals, so the final result of the macro is equivalent to "%10.3f"
.
The same is done when constructing the format string:
printf(FLOAT_FMT ", " FLOAT_FMT ", " FLOAT_FMT ", " FLOAT_FMT,
myFloat1, myFloat2, myFloat3, myFloat4);
is effectively:
printf("%10.3f, %10.3f, %10.3f, %10.3f",
myFloat1, myFloat2, myFloat3, myFloat4);
A drawback of this solution is that juxtapositions of strings and macros are somewhat hard to read.
Upvotes: 2
Reputation: 206697
You can create the format string at run time and use it as many times as you need.
// Create the format string.
char formatString[100]; // Make it large enough.
sprintf(formatString, "%%%d.%df", FLOAT_FORMAT_SIZE, FLOAT_FORMAT_PRECISION);
// Use the format string to print a float
printf(formatString, myFloat1);
printf(formatString, myFloat2);
printf(formatString, myFloat3);
printf(formatString, myFloat4);
Upvotes: 2