Reputation: 59
I have a structure defined as such
typedef struct box
{
float x;
float y;
float z;
float volume;
int order;
}box;
and the file I'm given presents the data this way:
3 2.5 3
4.4 5 6
8.7 6.5 9.5
2.5 6.5 7.3
7.6 5.1 6.2
with each number representing a dimension of a parallelepiped
printf("%f %f %f %f\n", A[i]->x, A[i]->y, A[i]->z, A[i]->volume)
with i being a for loop and A the array where I store the structures gives me this output
3 2.5 3 22.5
4.4 5 6 132
8.7 6.5 9.5 537.225
2.5 6.5 7.3 118.625
5.7 8.7 9.8 485.982
7.6 5.1 6.2 240.312
How can I print it in an organized way like the input file?
one problem is that the input might also look like differently, for example.
5.244231 2.231432 7.232432
so I need my printout to accommodate the different lengths of numbers, the formats don't mix though, so for example it can't be 5 4.52653 3.674
Upvotes: 2
Views: 898
Reputation: 153517
accommodate the different lengths of numbers
float
values typically range from +/-1.4...e-45F to +/-3.4...e+38F, +/-0, infinity and various not-a-numbers.
"%f"
prints with 6 digits after the .
as so prints 0.000000
for many small float
and with many uninformative digits when very large as is thus not satisfactory.
For a general printing, begin by using "%g"
which adapts its output (using exponential notation for large and wee values) and truncates trailing zeros.
Use a width, which specifies the minimum characters to print.
float
typical requires up to 9 significant digits to distinguish from other float
. Consider a precision.
Use source concatenation to allow for only 1 definition of the desired format.
// printf("%f %f %f %f\n", A[i]->x, A[i]->y, A[i]->z, A[i]->volume)
#define FMT_F "%-15.9g"
// ^ precision
// ^^ width
// ^ left justify
printf(FMT_F " " FMT_F " " FMT_F " " FMT_F "\n",
A[i]->x, A[i]->y, A[i]->z, A[i]->volume)
// Example output
4.4 5 6 132
8.7 6.5 9.5 537.225
1.40129846e-45 -3.40282347e+38 123.4 -5.88417018e-05
More advanced code would use a compiler dependent width and precision.
Upvotes: 1
Reputation: 12988
You can specify the column width by putting a number between the %
and f
so, for example:
printf("||%10f|%10f|%10f|%10f||\n", box.x, box.y, box.z, box.volume);
would print:
|| 3.100000| 2.000000| 9.500000| 58.899998||
Notice these are right aligned. If you put a minus sign in front of the column width, you can make it left aligned.
printf("||%10f|%10f|%10f|%-10f||\n", box.x, box.y, box.z, box.volume);
will print:
|| 3.100000| 2.000000| 9.500000|58.899998 ||
It's also useful to know that you can control the number of digits shown after the decimal point by specifying .<count>
after the column width:
printf("||%10.2f|%10.2f|%10.2f|%10.4f||\n", box.x, box.y, box.z, box.volume);
will print
|| 3.10| 2.00| 9.50| 58.9000||
Update: It looks like you've added more details to your question. Here's additional information that you could use.
The width and precision numbers do not have to be constants in the format string. You can replace any or all of them with an asterisk '*' and supply a variable like this:
for (int precision = 1; precision < 5; ++precision) {
int width = precision * 4;
printf("||%*.*f|%*.*f|%*.*f|%*.*f||\n",
width, precision, box.x,
width, precision, box.y,
width, precision, box.z,
width, precision, box.volume);
}
This will produce:
|| 3.1| 2.0| 9.5|58.9||
|| 3.10| 2.00| 9.50| 58.90||
|| 3.100| 2.000| 9.500| 58.900||
|| 3.1000| 2.0000| 9.5000| 58.9000||
A quick way of determining the number of digits before the decimal point is:
int digits = (int)log(values[i])/log(10) + 1;
so if you run through your data beforehand you can decide an appropriate column width.
As for digits after the decimal point, that decision is up to you; the quirks of floating point representations can lead to result an end-user might find confusing. For example in the data I used above 3.1 * 2.0 * 9.5 is printed as 58.899998 if allowed to print all digits, whereas 5.9 is probably what end-users would expect.
Upvotes: 1
Reputation: 141145
I need my printout to accommodate the different lengths of numbers, the formats don't mix though, so for example it can't be 5 4.52653 3.674
That's simple (ok, it's not that simple)! Just:
For inspiration, see util-linux/column.c utility.
And, well, a code sample: with array of two ints with different values, let's print them:
#define _GNU_SOURCE 1 // for asprintf
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct {
int a;
int b;
} data_t;
#define DATA_ELEMS 2 // the count of output columns
#define COUNT 3 // the count of output rows
int main() {
// some data we are going to print
const data_t data[COUNT] = {
{rand(),rand()},
{rand(),rand()},
{1,2},
};
// Print all elements to this array.
char *strs[COUNT][DATA_ELEMS] = {0};
for (size_t i = 0; i < COUNT; ++i) {
const data_t *it = &data[i];
asprintf(&strs[i][0], "%d", it->a);
asprintf(&strs[i][1], "%d", it->b);
// TODO: error checking
}
// For each column in the output array:
// get the longest element of this column.
size_t collens[DATA_ELEMS] = {0};
for (size_t i = 0; i < COUNT; ++i) {
for (size_t j = 0; j < DATA_ELEMS; ++j) {
if (collens[j] < strlen(strs[i][j])) {
collens[j] = strlen(strs[i][j]);
}
}
}
for (size_t i = 0; i < COUNT; ++i) {
// Print each column padded with spaces to the longest element.
for (size_t j = 0; j < DATA_ELEMS; ++j) {
printf("%*s", (int)collens[j], strs[i][j]);
// Print spaces between columns.
putchar(j + 1 == DATA_ELEMS ? '\n' : ' ');
}
}
for (size_t i = 0; i < COUNT; ++i) {
for (size_t j = 0; j < DATA_ELEMS; ++j) {
free(strs[i][j]);
}
}
}
will for example output on godbolt:
1804289383 846930886
1681692777 1714636915
1 2
Upvotes: 1