fprintf that uses constant space - c

I have the problem with fprintf. What I want is to use as much of space as possible staying in the same column but that means changing amount of decimal floating numbers. I have a bit problem to describe it so maybe just example:
0.001234
0.123456
1.234567
12.34567
123.4567
I tried to play with g and f options but it didn't work for me (i.e. things like %8.6g or %8.6f).

This is a tricky to survive corner cases.
With different precision specifiers, the number of digits of the left side of the decimal point changes.
printf("%.3f", 9.996) --> "9.996";
printf("%.2f", 9.996) --> "10.00"; // Same print width with different precision.
Given the various rounding modes, +/- numbers, NaN, INF, and details of the typical binary representation of floating point numbers, attempting to predict the width a printf() for a given floating point number is, at best, an approximation. Rather than out-think printf(), simple sprintf() with a best guess precision. Use sprintf() return value and adjust the precision as needed.
Solution:
Estimate width.
Print to a buffer.
Adjust if needed.
.
#include <float.h>
int fprintf_Width(FILE *outf, double a, int width) {
// Subtract 2 for a potential leading "0."
int prec = width - 2;
if (prec < 0 || prec > 100 /* TBD upper bound */)
return 0; // `width` out of range
// By some means, determine the approximate precision needed.
char buf[1 + 1 + DBL_MAX_10_EXP + 1 + prec + 1];
int len = sprintf(buf, "%#.*f", prec, a);
prec -= len - width;
if (prec < 0)
return 0; // `a` too big
// Try it out
len = sprintf(buf, "%#.*f", prec, a);
// Adjust as needed
if (len > width) {
prec--;
if (prec < 0)
return 0; // `a` too big
sprintf(buf, "%#.*f", prec, a);
}
len = fprintf(outf, "%s", buf);
return len;
}

Acknowledgement: This approach fails with certain numbers with certain width-specifications, as pointed out in the comments.
One thing you could do is to calculate the amount of digits before the decimal point, and then use the printf Precision Specification to truncate the digits that would make the number too long.
You could create a custom function, taking the FILE pointer to use with fprintf, float nubmer that you want to print and the desired amount of characters you want to have; 3 arguments.
The next thing you should do is to determine the amount of digits there before the decimal point. Having a new int variable and putting the float you have into it, then dividing the new int by 10s until it is zero should do it. But beware, 0.001234 also has one digit before the decimal point, although its int evaluation would not show one.
Then all you'd have to do is to reduce the width received by the amount of digits before the decimal point, plus 1 for the decimal point itself (or rather minus in this case), and this shall be your Precision Specification for the fprintf you'll use.
So, in the end, your fprintf should look somewhat like this with this method:
fprintf( fp, "%.*f", Width - digitsbeforeDpoint - 1 );
Here is an example that I've done, working as far as I can test. It also has some further precautions, making sure that every input is reasonable and assuring that the float given is qualified to print out the amount of digits you'd desire:
#include <stdio.h>
void fixdLengthfprintf( FILE * _File, float a, int width ) {
if ( !_File ) return;
if ( width < 3 ) return;
int b = a;
do {
width--;
b /= 10;
} while ( b );
width--;
if ( width > 0 ) fprintf( _File, "%.*f\n", width, a );
}
int main( ) {
float numbers[7] = { // prints as:
1.123456, // 1.123456
12.1234567, // 12.12346 (apparently rounded)
123456.0123456, // 123456.0 (heavily rounded)
1234567.0123456, // doesn't get printed
12345678.0123456, // doesn't get printed
123456789.0123456, // doesn't get printed
0.123456789, // 0.123457
};
// I don't allow them to get printed because else they'd look like:
// 1.123456
// 12.12346
// 123456.0
// 1234567
// 12345678.000000
// 123456792.000000
// 0.123457
for ( int i = 0; i < 7; i++ ) fixdLengthfprintf( stdout, numbers[i], 8 );
fflush( stdin );
getchar( );
return 0;
}

Related

How do I properly format printing row of numbers in c?

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
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.
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:
Allocate a 2d array of output strings.
Print all elements to this array.
For each column in the output array:
get the longest element of this column.
Print each column padded with spaces to the longest element.
Print spaces between columns and newlines between rows.
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
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.

Decimal precision using integer

I am programming a uC in C language and I need to show a float number with 4 precision digits. The thing here is that my number is not really a float type. I have the integer part and the decimal part of the number in two different integer variables. Let say: int digit and int decimal.
I tried using printf ("%d.%d"); That works fine when my decimal number is 6524, but the problem comes when it is exactly 65 since it doesnt show 4 decimals.
I tried using printf ("%d.%04d"); but when my decimal part is exactly 65 it shows 0065 which is not mathematically correct (I would need 6500)
I looked for any argument of printf which completes with zeros at the end but could not find anything. All of them complete with leading zeros which is not useful for me in this case.
I also though about checking if my number is minor that 10, 100 or 1000 and multiply it by 1000, 100 or 10 respectively. But it will not work when the decimal part is exactly 0, since 0*1000 will still be 0 and not 0000.
Any idea on how to solve this? Please let me know if I am not completely clear and I will provide more information
Thanks!
Since printf returns the number of characters printed, you can do it, somewhat clumsily, as follows:
printf("%d.", int_part);
int digits = printf("%d", frac_part);
while (digits++ < 4) putchar('0');
I have to say, though, that it is a very eccentric form of representing a floating point number, and you might seriously want to rethink it.
Another wired possibility is to convert the decimal part to a string and then fill it with 0:
int main() {
int i, d, len;
char p[10];
i = 189;
d = 51;
// convert the number to string and count the digits
snprintf(p, 10, "%d", d);
len = strlen(p);
while (len < 4) {p[len] = '0'; len++;}
p[len] = '\0';
fprintf(stdout, "%d.%s\n", i, p);
// you can also go back to int
d = atoi(p);
fprintf(stdout, "%d.%d\n", i, d);
}
Combine both your answers: multiply by 10, 100 or 1000 as necessary, but still print with %04d.

conversion in c

I'm new to C and I need to write a function in c which converts an integer to a string in the specified base and print it.
If I were given an input value of 1234 (base 10) it should return 2322 (base 8).
Here is the code structure I'm currently working on:
void int2ascii(int value, int base){
int a=0;
if (value > base) {
a = a + int2char(value); //recursive case
int2ascii(value/base, base); //base case
}
printf("%s\n",a);
}
The program won't run, can someone enlighten me?
Thanks
Analysis of your program
I noted first off that a is declared int, but it was being passed to printf with %s as the format specifier. %s designates the associated argument will be a string, which a is not. The result will be undefined behavior, and is a possible cause for your crash.
You do not specify what int2char() does, but let's assume that it converts a numeric "digit" into a corresponding char value. With that in mind, let us suppose its implementation is similar to:
int int2char(int d) {
return "0123456789abcdefghijklmnopqrstuvwxyz"[d];
}
In your code, you pass value to int2char(). In my hypothetical implementation, this would cause out-of-bounds access of the array, and thus undefined behavior. This is indicates a logic error, and another possible cause for your crash.
I note that if value is less than base, a remains 0. Probably, you really mean to compute a value for a even when value is less than base. This indicates another logic error.
Base conversion
The number dKdK-1 .. d0, where each di is in (0 .. 9), is a short form for &Sum;di×10i. To discover the base 10 digits of a number N, the process is:
di = ⌊N / 10i⌋ mod 10
But, as it turns out, you can replace 10 with some other base number to compute the digits for a number in that base:
di = ⌊N / Bi⌋ mod B
In your code, the recursive call that divides by the base represents the first part of the calculation. However, in your calculation of a, you were missing the "mod" part of the calculation.
Solution 1
Assuming base is from 2 to 36, and that your int2char(d) does something more or less as illustrated earlier:
void int2ascii(int value, int base){
int a=0;
a = a + int2char(value%base);
if (value > base) {
int2ascii(value/base, base);
}
printf("%c",a);
}
Because you are not passing a into your recursive call, you can only use it to store the current digit to be printed. So, move the code to store the value in a out of the if check, since you always want a sensible value to print at the end. The digit in a is in the radix base, so you need the modulus result to get the right digit value. Since a represents a character, change the format string to match, and remove the \n so that all the digits end up on the same line.
Solution 2
The first solution is an attempt to leave most of your original code intact. Since a is initialized to 0, the extra addition does not make it incorrect. But, since the print doesn't happen until the very end, the value doesn't really need to be stored at all, and can be calculated at the point you actually want to print it. With that in mind, the program can be simplified to:
void int2ascii(int value, int base){
if (value > base) {
int2ascii(value/base, base);
}
printf("%c",int2char(value%base));
}
#include <stdio.h>
#include <limits.h>
char *int2ascii_aux(int n, int base, char *s){
//base {x| 2 <= x <= 36 }
static const char *table = "0123456789abcdefghijklmnopqrstuvwxyz";
if(n){
*--s = table[n % base];
return int2ascii_aux(n/base, base, s);
} else {
return s;
}
}
char *int2ascii(int n, int base){
//base {x| 2<= x <= 36 }
static char s[sizeof(int)*CHAR_BIT+1];
if(n <= 0){
*s = '0';
return s;
}
if(base < 2 || base > 36){
*s = '\0';
return s;
}
return int2ascii_aux(n, base, s + sizeof(s) -1);
}
int main(){
printf("%s\n", int2ascii(1234, 8));
return 0;
}

Getting printf() to drop the trailing ".0" of values

I need to print floating point numbers with the following formatting requirements:
5.12345 should display only 5.1
5.0 should only 5 (without the .0)
5.0176 should display only 5 (without the .01)
I thought printf() could do something like this... but now I can't seem to get it to work.
You can get kind of close to the results you want using "%g"
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("%.6g\n", 5.12345f);
printf("%.6g\n", 5.0f);
printf("%.6g\n", 5.0176f);
return 0;
}
Output:
5.12345
5
5.0176
"%g" will remove trailing zeros.
Sounds like you want to print 1 decimal place, and if that place is 0, drop it. This function should work fine:
// prints the float into dst, returns the number
// of chars in the manner of snprintf. A truncated
// output due to size limit is not altered.
// A \0 is always appended.
int printit(float f, char *dst, int max) {
int c = snprintf(dst, max, "%.1f", f);
if(c > max) {
return c;
}
// position prior to '\0'
c--;
while(dst[c] == '0') {
c--;
if(dst[c] == '.') {
c--;
break;
}
}
dst[c + 1] = '\0';
return c + 1;
}
int main(void) {
char num1[10], num2[10], num3[10];
printit(5.12345f, num1, 10);
printit(5.0f, num2, 10);
printit(5.0176f, num3, 10);
printf("%s\n%s\n%s\n", num1, num2, num3);
}
printf() can use formatting strings. Look at the width and precision options of the formatting strings:
printf("%.1f", 5.12345f);
This will print 5.1.
Unfortunately, it does not have the ability to automatically determine, without guidance, what to display. (For example, your last option there is unclear to me - why should it drop the ".0176", without you telling it you want no decimal points?)
None of the formatting operators support this, however you could filter the float through sprintf() first, then truncate the string where appropriate. This saves you the trouble of converting the float to a string and the logic to do the rest is easy.
Not the way you've described it, no. If you want conditional decimal places, you have to do it yourself.
it's been a while since I've mucked with printf formats, but this appears to work in most cases
char *BuildConditionalFormat(double val)
{
int tenths = (int)(val * 10) % 10;
if (tenths == 0)
return ".0f";
return ".1f";
}
/* test rig */
float f = 5.0;
printf(BuildConditionalFormat(f), f); /* -> 5 */
f = 5.13;
printf("\n");
printf(BuildConditionalFormat(f), f); /* -> 5.1 */
printf("\n");
This abides by your rules, but will also provide an interesting lesson in why floating point stinks because 5.1 -> 5. Why? Because 5.1 doesn't represent cleanly (on my machine) as a float - it's 5.09999 and some more change.
Chances are you need to learn about floor() and ceil() too...
You can tell printf to print specified number of digits after '.' ,but if you want to differ the format basing on the value you have to write code to distinguish cases interesting for you.

Converting a number from a given base to base 10

I am trying to convert from any base to base 10. For an input of 010111 base 2 it gives me 1, and for 35 base 9 it gives me 18 which should be 38. Any suggestions?
#include<stdio.h>
#include<math.h>
#include<string.h>
#define LENGTH 6
double pow( double x, double power );
int main()
{
char input[LENGTH+1] ;
int base;
unsigned decValue ;
char values[] = "0123456789ABCDEFG";
int i;
puts( "Enter a value, and its base: " );
scanf( "%s", &input );
scanf( "%i", &base );
for( i = LENGTH-1 ; i >= 0; i-- )
{
int index = strchr(values, input[ i ] ) - values;
decValue += index * pow( base , i - LENGTH + 1 );
}
printf( "%s (base-%u) equals %u \n", input, base, decValue );
return 0;
}
The thing that troubles me here the most is your mixture of floating point arithmetic into an integer problem. Not only is it less efficient, but when the result is converted back to an int, there's the possibility it might get rounded down or something silly. Hypothetical example:
double n = pow(2,3); //becomes 7.99999999999999999999999 for some silly reason
int in = n; //rounds down to 7
Although this might not even be happening in your case, be wary of integer <-> floating point conversions, and avoid them when they are unnecessary.
A better solution is to build up the power incrementally. If I were you, I'd iterate from beginning to end of the number. Pseudocode:
let s be the input string's digits;
let r be the radix;
let n be the resulting number;
n=0;
for (i=0; i<s.size(); i++) {
n *= radix;
n += s[i];
}
The concept is that you are picking up digits and shifting them into the number. E.g. 123 in decimal:
1
(shift 1 left, then add 2)
12
(shift 12 left, then add 3)
123
Shifting left, in the context of integers in a given base, is simply multiplying by the radix.
Since i < LENGTH, i - LENGTH + 1 is negative and the pow is therefore 0.
So, you should use pow( base , LENGTH - i - 1 ) -- that one's the biggie. Other big bugs: you should use strlen(input) wherever you're using LENGTH; and you should initialize decValue to 0. There may be others, I stopped looking after the first three (this IS after all California, land of the "Three Strikes, You're Out" law;-).
My suggestion: Dont reinvent the wheel.
See strtoul function:
#include <stdlib.h>
#include <stdio.h>
int main(){
/* first arg: number in base n to convert to base 10
* third arg: base n
*/
long int b10= strtoul("111",NULL,2);
printf("%ld\n",b10);
return 0;
}
You're adding to decValue before you ever assign anything to it. I didn't look too closely at your logic but that stood out immediately. Depending on platform that may cause you issues.
A few comments:
As written, your code doesn't support "any base", only bases up to 17 (an odd place to stop).
strchr() can return NULL. You should never assume that it won't, especially when you're feeding it direct user input.
Lowercase letters are nice, and you should support them. Simply convert the relevant character to uppercase with toupper(), and then you can recognize both a and A as hexidecimal 10.
In the interest of coding practices, I would suggest creating a function to perform the relevant conversion, rather than doing all the dirty work in main().
If you're doing this as a learning exercise, ignore this paragraph, but you appear to essentially be rewriting the standard library function strtol(), which converts a string to a long for arbitrary bases. It's pretty nice, actually, and has some functionality that you could incorporate into your code if you wanted.
Couple things: don't take the address of input (input, an array, already degrades to the pointer you want; taking a pointer of it will result in a pointer to an array, not what you want); and you need to make sure not to overflow the input buffer when reading into it.
If this is not homework, you should just use strtol():
#include <stdlib.h>
#include <stdio.h>
#define Str(x) #x
#define Xstr(x) Str(x)
int main()
{
char input[LENGTH+1] ;
int base;
long decValue;
char *endptr;
puts( "Enter a value, and its base: " );
scanf( "%" Xstr(LENGTH) "s", input ); /* reads at most LENGTH characters */
scanf( "%i", &base );
decValue = strtol( input, &endptr, base );
assert( *endptr == '\0' ); /* if there are any characters left, then string contained invalid characters */
printf( "%s (base-%u) equals %ld\n", input, base, decValue );
return 0;
}

Resources