Avoid trailing zeros in printf ()

I keep stumbling about format specifiers for the printf () family of functions. I want to be able to print double (or float) with the maximum given number of digits after the decimal point. If I use:

printf("%1.3f", 359.01335); printf("%1.3f", 359.00999); 

I get

 359.013 359.010 

Instead of the desired

 359.013 359.01 

Can someone help me?

+81
c printf
Nov 10 '08 at
source share
12 answers

This cannot be done using standard printf format specifiers. The closest you could get would be:

 printf("%.6g", 359.013); // 359.013 printf("%.6g", 359.01); // 359.01 

but ".6" is the total numerical width, therefore

 printf("%.6g", 3.01357); // 3.01357 

breaks it.

What you can do is sprintf("%.20g") number in the string buffer, then manipulate the string to have only N characters after the decimal point.

Assuming your number is in the num variable, the following function removes everything except the first N decimal places, and then removes the trailing zeros (and the decimal point if they are all zeros).

 char str[50]; sprintf (str,"%.20g",num); // Make the number. morphNumericString (str, 3); : : void morphNumericString (char *s, int n) { char *p; int count; p = strchr (s,'.'); // Find decimal point, if any. if (p != NULL) { count = n; // Adjust for more or less decimals. while (count >= 0) { // Maximum decimals allowed. count--; if (*p == '\0') // If there less than desired. break; p++; // Next character. } *p-- = '\0'; // Truncate string. while (*p == '0') // Remove trailing zeros. *p-- = '\0'; if (*p == '.') { // If all decimals were zeros, remove ".". *p = '\0'; } } } 



If you are not comfortable with the truncation aspect (which will turn 0.12399 to 0.123 , rather than 0.124 it to 0.124 ), you can actually use the rounding capabilities already provided by printf . You just need to parse the number first to dynamically create the width, then use them to turn the number into a string:

 #include <stdio.h> void nDecimals (char *s, double d, int n) { int sz; double d2; // Allow for negative. d2 = (d >= 0) ? d : -d; sz = (d >= 0) ? 0 : 1; // Add one for each whole digit (0.xx special case). if (d2 < 1) sz++; while (d2 >= 1) { d2 /= 10.0; sz++; } // Adjust for decimal point and fractionals. sz += 1 + n; // Create format string then use it. sprintf (s, "%*.*f", sz, n, d); } int main (void) { char str[50]; double num[] = { 40, 359.01335, -359.00999, 359.01, 3.01357, 0.111111111, 1.1223344 }; for (int i = 0; i < sizeof(num)/sizeof(*num); i++) { nDecimals (str, num[i], 3); printf ("%30.20f -> %s\n", num[i], str); } return 0; } 

The whole point of nDecimals() in this case is to work out the field width correctly, and then format the number using a format string based on this. The test harness main() shows this in action:

  40.00000000000000000000 -> 40.000 359.01335000000000263753 -> 359.013 -359.00999000000001615263 -> -359.010 359.00999999999999090505 -> 359.010 3.01357000000000008200 -> 3.014 0.11111111099999999852 -> 0.111 1.12233439999999995429 -> 1.122 

Once you have a properly rounded value, you can pass it again to morphNumericString() to remove trailing zeros by simply changing:

 nDecimals (str, num[i], 3); 

at

 nDecimals (str, num[i], 3); morphNumericString (str, 3); 

(or calling morphNumericString at the end of nDecimals , but in this case I would probably just combine the two into one function), and you will get:

  40.00000000000000000000 -> 40 359.01335000000000263753 -> 359.013 -359.00999000000001615263 -> -359.01 359.00999999999999090505 -> 359.01 3.01357000000000008200 -> 3.014 0.11111111099999999852 -> 0.111 1.12233439999999995429 -> 1.122 
+67
Nov 10 '08 at 13:01
source share

To get rid of trailing zeros, you must use the format "% g":

 float num = 1.33; printf("%g", num); //output: 1.33 

After the question was clarified a bit, the suppression of zeros was not the only thing that was asked, but it was also required to limit the output to three decimal places. I think this cannot be done with sprintf format strings. As Pax Diablo noted, a string of manipulation is required.

+43
Nov 10 '08 at 12:45
source share

I like the answer R. slightly corrected:

 float f = 1234.56789; printf("%d.%.0f", f, 1000*(f-(int)f)); 

'1000' defines precision.

Rounding power 0.5.

EDIT

Well, this answer has been edited several times, and I lost track of what I thought a few years ago (and initially it did not fill out all the criteria). So, here is the new version (which fills all the criteria and correctly processes negative numbers):

 double f = 1234.05678900; char s[100]; int decimals = 10; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf("10 decimals: %d%s\n", (int)f, s+1); 

And test cases:

 #import <stdio.h> #import <stdlib.h> #import <math.h> int main(void){ double f = 1234.05678900; char s[100]; int decimals; decimals = 10; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf("10 decimals: %d%s\n", (int)f, s+1); decimals = 3; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf(" 3 decimals: %d%s\n", (int)f, s+1); f = -f; decimals = 10; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf(" negative 10: %d%s\n", (int)f, s+1); decimals = 3; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf(" negative 3: %d%s\n", (int)f, s+1); decimals = 2; f = 1.012; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf(" additional : %d%s\n", (int)f, s+1); return 0; } 

And the test output:

  10 decimals: 1234.056789 3 decimals: 1234.057 negative 10: -1234.056789 negative 3: -1234.057 additional : 1.01 

Now all the criteria are met:

  • fixed maximum number of decimal places after zero.
  • zero zeros are removed
  • he does it mathematically correctly (right?)
  • works (now) when the first decimal digit is zero

Unfortunately, this answer is two-line, since sprintf does not return a string.

+13
Nov 22 '10 at 16:10
source share

Something like this (there may have rounding errors and problems with a negative value requiring debugging left as an exercise for the reader):

 printf("%.0d%.4g\n", (int)f/10, f-((int)f-(int)f%10)); 

It is a bit programmatic, but at least it does not force you to do any string manipulations.

+2
Jul 08 '10 at 7:45
source share

A simple solution, but it does the job, assigns a known length and precision and avoids the exponential format (which is a risk when using% g):

 // Since we are only interested in 3 decimal places, this function // can avoid any potential miniscule floating point differences // which can return false when using "==" int DoubleEquals(double i, double j) { return (fabs(i - j) < 0.000001); } void PrintMaxThreeDecimal(double d) { if (DoubleEquals(d, floor(d))) printf("%.0f", d); else if (DoubleEquals(d * 10, floor(d * 10))) printf("%.1f", d); else if (DoubleEquals(d * 100, floor(d* 100))) printf("%.2f", d); else printf("%.3f", d); } 

Add or remove "elses" if you want a maximum of 2 decimal places; 4 decimal places; and etc.

For example, if you need two decimal places:

 void PrintMaxTwoDecimal(double d) { if (DoubleEquals(d, floor(d))) printf("%.0f", d); else if (DoubleEquals(d * 10, floor(d * 10))) printf("%.1f", d); else printf("%.2f", d); } 

If you want to specify the minimum width for aligning the fields, increase them if necessary, for example:

 void PrintAlignedMaxThreeDecimal(double d) { if (DoubleEquals(d, floor(d))) printf("%7.0f", d); else if (DoubleEquals(d * 10, floor(d * 10))) printf("%9.1f", d); else if (DoubleEquals(d * 100, floor(d* 100))) printf("%10.2f", d); else printf("%11.3f", d); } 

You can also convert this to a function in which you pass the desired field width:

 void PrintAlignedWidthMaxThreeDecimal(int w, double d) { if (DoubleEquals(d, floor(d))) printf("%*.0f", w-4, d); else if (DoubleEquals(d * 10, floor(d * 10))) printf("%*.1f", w-2, d); else if (DoubleEquals(d * 100, floor(d* 100))) printf("%*.2f", w-1, d); else printf("%*.3f", w, d); } 
+2
Feb 28 '13 at 4:08
source share

I am looking for a string (starting with the rightmost one) for the first character in the range from 1 to 9 (ASCII value 49 - 57 ), then null (set to 0 ) each char to the right of it - see below:

 void stripTrailingZeros(void) { //This finds the index of the rightmost ASCII char[1-9] in array //All elements to the left of this are nulled (=0) int i = 20; unsigned char char1 = 0; //initialised to ensure entry to condition below while ((char1 > 57) || (char1 < 49)) { i--; char1 = sprintfBuffer[i]; } //null chars left of i for (int j = i; j < 20; j++) { sprintfBuffer[i] = 0; } } 
+2
Mar 08 '13 at 1:27
source share

Here is my first attempt to answer:

 void
 xprintfloat (char * format, float f)
 {
   char s [50];
   char * p;

   sprintf (s, format, f);
   for (p = s; * p; ++ p)
     if ('.' == * p) {
       while (* ++ p);
       while ('0' == * - p) * p = '\ 0';
     }
   printf ("% s", s);
 }

Known errors: possible buffer overflow depending on the format. If "." present only for the reason that an incorrect result may occur.

0
Nov 10 '08 at 14:49
source share

Slight variation above: -

  • Eliminates the period for the case (10000.0).
  • A break is processed after the first period.

Code here: -

 void EliminateTrailingFloatZeros(char *iValue) { char *p = 0; for(p=iValue; *p; ++p) { if('.' == *p) { while(*++p); while('0'==*--p) *p = '\0'; if(*p == '.') *p = '\0'; break; } } } 

It still has potential for overflow, so be careful: P

0
Feb 01 2018-10-01T00
source share

Why not just do it?

 double f = 359.01335; printf("%g", round(f * 1000.0) / 1000.0); 
0
Aug 14 '14 at 16:54
source share

I found problems in some of the posted solutions. I put this together based on the answers above. It seems to work for me.

 int doubleEquals(double i, double j) { return (fabs(i - j) < 0.000001); } void printTruncatedDouble(double dd, int max_len) { char str[50]; int match = 0; for ( int ii = 0; ii < max_len; ii++ ) { if (doubleEquals(dd * pow(10,ii), floor(dd * pow(10,ii)))) { sprintf (str,"%f", round(dd*pow(10,ii))/pow(10,ii)); match = 1; break; } } if ( match != 1 ) { sprintf (str,"%f", round(dd*pow(10,max_len))/pow(10,max_len)); } char *pp; int count; pp = strchr (str,'.'); if (pp != NULL) { count = max_len; while (count >= 0) { count--; if (*pp == '\0') break; pp++; } *pp-- = '\0'; while (*pp == '0') *pp-- = '\0'; if (*pp == '.') { *pp = '\0'; } } printf ("%s\n", str); } int main(int argc, char **argv) { printTruncatedDouble( -1.999, 2 ); // prints -2 printTruncatedDouble( -1.006, 2 ); // prints -1.01 printTruncatedDouble( -1.005, 2 ); // prints -1 printf("\n"); printTruncatedDouble( 1.005, 2 ); // prints 1 (should be 1.01?) printTruncatedDouble( 1.006, 2 ); // prints 1.01 printTruncatedDouble( 1.999, 2 ); // prints 2 printf("\n"); printTruncatedDouble( -1.999, 3 ); // prints -1.999 printTruncatedDouble( -1.001, 3 ); // prints -1.001 printTruncatedDouble( -1.0005, 3 ); // prints -1.001 (shound be -1?) printTruncatedDouble( -1.0004, 3 ); // prints -1 printf("\n"); printTruncatedDouble( 1.0004, 3 ); // prints 1 printTruncatedDouble( 1.0005, 3 ); // prints 1.001 printTruncatedDouble( 1.001, 3 ); // prints 1.001 printTruncatedDouble( 1.999, 3 ); // prints 1.999 printf("\n"); exit(0); } 
0
Oct. 31 '15 at 5:45
source share

Some of the highly rated solutions offer the %g printf conversion specifier. This is wrong, because there are times when %g will give scientific notation. Other solutions use math to print the desired number of decimal digits.

I think the easiest solution is to use sprintf with the %f conversion specifier and manually remove trailing zeros and possibly a decimal point from the result. Here's the solution to C99:

 #include <stdio.h> #include <stdlib.h> char* format_double(double d) { int size = snprintf(NULL, 0, "%.3f", d); char *str = malloc(size + 1); snprintf(str, size + 1, "%.3f", d); for (int i = size - 1, end = size; i >= 0; i--) { if (str[i] == '0') { if (end == i + 1) { end = i; } } else if (str[i] == '.') { if (end == i + 1) { end = i; } str[end] = '\0'; break; } } return str; } 

Note that the characters used for numbers and the decimal separator depend on the current locale. The above code assumes C or English.

0
Mar 24 '16 at 2:39 on
source share

Your code is rounded to three decimal places due to a ".3" before f

 printf("%1.3f", 359.01335); printf("%1.3f", 359.00999); 

Thus, if the second line is rounded to two decimal places, you should change it to this:

 printf("%1.3f", 359.01335); printf("%1.2f", 359.00999); 

This code will produce the desired results:

 359.013 359.01 

* Please note that it is assumed that you already have printing on separate lines; if not, the following will prevent printing on one line:

 printf("%1.3f\n", 359.01335); printf("%1.2f\n", 359.00999); 

The following program source code was my test for this answer

 #include <cstdio> int main() { printf("%1.3f\n", 359.01335); printf("%1.2f\n", 359.00999); while (true){} return 0; } 
-one
Sep 24 '13 at 22:17
source share



All Articles