I want to output my floats without the ending zeros.
Example: float 3.570000 should be outputted as 3.57
and float 3.00000 should be outputted as 3.0 (so here would be the exception!)
I want to output my floats without the ending zeros.
Example: float 3.570000 should be outputted as 3.57
and float 3.00000 should be outputted as 3.0 (so here would be the exception!)
This is not possible with standard printf semantics. In, the past, I've had to do this by outputting to a string (with something like "%.20f"
) then post-processing the string.
Something like this is probably what you're looking for:
#include <stdio.h>
void morphNumericString (char *s) {
char *p;
int count;
// Find decimal point, if any.
p = strchr (s,'.');
if (p == NULL) {
// No decimal, just add one fractional position.
strcat (s, ".0");
} else {
// Decimal, start stripping off trailing zeros.
while (s[strlen(s)-1] == '0') {
s[strlen(s)-1] = '\0';
}
// If all fractional positions were zero, add one.
if (s[strlen(s)-1] == '.') {
strcat (s, "0");
}
}
}
int main (int argc, char *argv[]) {
char str[100];
int i;
for (i = 1; i < argc; i++) {
strcpy (str, argv[i]);
morphNumericString (str);
printf ("[%s] -> [%s]\n", argv[i], str);
}
return 0;
}
The code runs through each of it's arguments, morphing each one in turn. The following transcript shows how it works:
pax> ./qq 3.750000 12 12.507 47.90 56.0000000 76.0 0
[3.750000] -> [3.75]
[12] -> [12.0]
[12.507] -> [12.507]
[47.90] -> [47.9]
[56.0000000] -> [56.0]
[76.0] -> [76.0]
[0] -> [0.0]
You should be aware however that, if using floats or doubles, you'll have to watch out for the normal floating point inaccuracies. On my system 3.57 is actually 3.5699999999999998401, which won't be truncated at all.
Of course, you can get around that problem by using a less-specific number of output digits in the sprintf
, something less than the actual accuracy of the floating point point. For example, "%.10f"
on my system outputs 3.5700000000 which will be truncated. Adding the following lines to main
:
sprintf (str, "%.10f", 3.57);
morphNumericString (str);
printf ("[%.10f] -> [%s]\n", 3.57, str);
sprintf (str, "%.10f", 3.0);
morphNumericString (str);
printf ("[%.10f] -> [%s]\n", 3.0, str);
will result in the following added output:
[3.5700000000] -> [3.57]
[3.0000000000] -> [3.0]
as per your test data.
One other possibility (if your input range and precision can be controlled) is to use the g
format specifier. This outputs in either f
format where the precision is the maximum number of digits (rather than fixed number like f
) or exponential format (e
).
Basically, it prefers the non-exponential output format as long as all the information is shown. It will switch to exponential only if that will deliver more information. A simple example is the format string "%.4g"
with 3.7
and .0000000004
. The former would be printed as 3.7
, the latter as 4e-10
.
Update: for those more concerned about performance than robustness or readability, you could try the following (unnecessary in my opinion but to each their own):
void morphNumericString (char *s) {
char *p = strchr (s,'.');
if (p == NULL) {
strcat (s, ".0");
return;
}
p = &(p[strlen(p)-1]);
while ((p != s) && (*p == '0') && (*(p-1) != '.'))
*p-- = '\0';
}
It may be faster but, given the extreme optimisations I've seen modern compilers do, you can never be too sure. I tend to code for readability first and only worry about speed when it becomes an issue (YAGNI can apply equally to performance as well as functionality).
A more efficient and (in my opinion) clearer form of paxdiablo's morphNumericString(). Sorry not compiled or tested.
void morphNumericString( char *s )
{
char *p, *end, *decimal, *nonzero;
// Find the last decimal point and non zero character
end = p = strchr(s,'\0');
decimal = nonzero = NULL;
while( p > s )
{
p--;
if( !nonzero && *p!='0' )
{
nonzero = p;
}
if( !decimal && *p=='.' )
{
decimal = p;
break; // nonzero must also be non NULL, so stop early
}
}
// eg "4.3000" -> "4.3"
if( decimal && nonzero && nonzero>decimal )
*(nonzero+1) = '\0';
// eg if(decimal) "4.0000" -> "4.0"
// if(!decimal) "4" -> "4.0"
else
strcpy( decimal?decimal:end, ".0" );
}
My approach is:
implement a function trim(char c) to eliminate the trail 'c', for examaple:
void trim(std::string &str, char c) { size_t len = str.length();
const char *s = str.c_str();
const char *p = s + len - 1;
while (p != s && *p == c) {
-- p;
}
++ p;
size_t end = p - s;
str = str.substr(0, end);
}
char buf[32]; printf(buf, "%f", 0.01); std::string s(buf); trim(buf, '0');
Here's another string modification function. I did test it.
It's longer than Bill's and Diablo's, but it handles trailing 9's as well as 0's and should perform well.
Leaves a single trailing zero after the dot. You'll have to truncate using sprintf
to get proper trailing 9's.
void morphNumericString( char *s ) {
char *point = s;
while ( * point && * point != '.' ) ++ point;
char *last = strchr( point, 0 );
if ( point == last ) {
* point = '.';
++ last;
}
if ( point == last - 1 ) {
* last = '0';
} else {
-- last;
if ( * last == '0' ) {
while ( * last == '0' ) -- last;
} else if ( * last == '9' ) {
while ( * last == '9' || * last == '.' ) {
if ( * last == '9' ) * last = '0';
-- last;
}
( * last ) ++;
}
if ( last < point + 1 ) last = point + 1;
}
* ++ last = 0;
}
Edit: Yikes, this fails on input like 999.999. Left as an exercise to the reader ;v)
It might be easier to calculate the fractional part directly:
double value= -3.57;
double int_part;
double frac= modf(value, &int_part);
int64 int_part_as_int= int_part;
int significant_digits= 10;
int64 fractional_scale= pow(10., significant_digits);
int64 fraction_magnitude= fabs(frac)*fractional_scale + 0.5;
fractional_magnitude/fractional_scale
will be the fraction rounded to significant_digits
sig figs. Even with doubles, this is guaranteed not to overflow.
Formatting the fraction should be straightforward.