floating point number formatting question
Hi everyone:
Say, if I want to format a floating point number pretty much like it's done in mathematics (see example below). I do not need precision of more than two decimal numbers. Is there a printf formatting specifier that can do this?
Examples:
1.0000 becomes "1"
1.30 becomes "1.3"
1.32 becomes "1.32"
1.38 becomes "1.38"
1.322 becomes "1.32"
1.325 becomes "1.33"
Re: floating point number formatting question
One way:
Code:
float Num=1.333f;
char Buf[128]={0};
sprintf(Buf,"%.2f",Num);
// Buf = "1.33"
Cheers
Re: floating point number formatting question
Quote:
Originally Posted by
golanshahar
One way:
Code:
float Num=1.333f;
char Buf[128]={0};
sprintf(Buf,"%.2f",Num);
// Buf = "1.33"
Cheers
I think this will fail test cases 1, 2 and 6.
Re: floating point number formatting question
"%.3g" can print all those values that you wanted. However, the precision here indicates the number of digits to be printed, before and after the decimal point. So if you're numbers have 2 digits before the decimal point, you must use "%.4g". So this can only work if you always have the same number of digits, and I guess that's not the case.
Re: floating point number formatting question
Thank you, guys.
No, neither "%.2f" nor "%.4g" will solve it. The first option simply uses 2 decimal numbers and the 'g' option can switch to the exponential output that I do not need in my case.
So, having done some research I see that there's no easy way to do what I need. So now the question is, what is the easiest way to do this with a custom function?
Something like this?
Code:
void print_custom_float(double fValue)
{
if((int)fValue == fValue)
{
_tprintf(_T("%d"), (int)fValue);
}
else if((int)(fValue * 10) == fValue * 10)
{
_tprintf(_T("%.1f"), fValue);
}
else
{
_tprintf(_T("%.2f"), fValue);
}
}
Re: floating point number formatting question
Quote:
if((int)fValue == fValue)
You never should compare floats like this. :) Actually it should be delta comparison:
Code:
if(abs(fValue - (int)fValue) < 0.000001)
Re: floating point number formatting question
Quote:
Originally Posted by
Igor Vartanov
You never should compare floats like this. :) Actually it should be delta comparison:
Code:
if(abs(fValue - (int)fValue) < 0.000001)
Only to get the correct result in this case the epsilon should be 0.005. That way 1.001 will be printed as 1, instead of 1.00.
Re: floating point number formatting question
Yes, good point, both of you. I posted it too quick w/o giving it a second thought. It should probably be re-written to something like this:
Code:
#include <Math.h>
void print_custom_float(double fValue)
{
if(fabs(fValue - (__int64)fValue) < 0.001) //For 2 decimal numbers
{
_tprintf(_T("%I64d"), (__int64)fValue);
}
else
{
int nValueMult100 = (int)(fabs(fValue - (__int64)fValue) * 100.0 + 0.5);
if(nValueMult100 % 10)
{
_tprintf(_T("%.2f"), fValue);
}
else
{
_tprintf(_T("%.1f"), fValue);
}
}
}
Unless someone can come up with a faster (less straightforward) solution?
Re: floating point number formatting question
Quote:
Originally Posted by
ahmd
So, having done some research I see that there's no easy way to do what I need.
You could format the output to always have exactly 2 decimal places, even if they are 0, easily enough. This is done with std::fixed for ostreams and, I think, %f for printf().
Then, once you've created this output string, you can simply drop trailing characters which are '0' (and '.' if you reach it, but stop there). Then write the modified string to the screen.
Re: floating point number formatting question
Quote:
Originally Posted by
Lindley
You could format the output to always have exactly 2 decimal places, even if they are 0, easily enough. This is done with std::fixed for ostreams and, I think, %f for printf().
Then, once you've created this output string, you can simply drop trailing characters which are '0' (and '.' if you reach it, but stop there). Then write the modified string to the screen.
Thanks for your insight but I'd rather not deal with string representation of numbers. There are many reasons for that and mainly localization -- you don't know how a certain number may be converted to a string for a certain user locale.
Re: floating point number formatting question
Quote:
Originally Posted by
ahmd
Thanks for your insight but I'd rather not deal with string representation of numbers. There are many reasons for that and mainly localization -- you don't know how a certain number may be converted to a string for a certain user locale.
I agree with Lindley. Also, I am not aware of any locale in which the following won't work (this, of course, is not a guarantee):
Code:
void MyPrint(float Num)
{
char Buf[128]={0};
int length = sprintf(Buf, "%.2f", Num);
while(Buf[length-1] == '0')
Buf[--length] = 0;
if(!isdigit(Buf[length-1]))
Buf[--length] = 0;
printf("%s\n", Buf);
}
int _tmain(int argc, _TCHAR* argv[])
{
MyPrint(1.0000);
MyPrint(1.30);
MyPrint(1.32);
MyPrint(1.38);
MyPrint(1.322);
MyPrint(1.325);
return 0;
}
Re: floating point number formatting question
Well, that might be an alternate solution. Thanks.
Quote:
Originally Posted by
VladimirF
I agree with Lindley. Also, I am not aware of any locale in which the following won't work (this, of course, is not a guarantee)
Well, that's something that would alarm me -- not knowing if it'd work everywhere -- and that is why I prefer dealing with numbers rather than strings (if I have a choice, of course).
Re: floating point number formatting question
Quote:
Originally Posted by
ahmd
Well, that's something that would alarm me -- not knowing if it'd work everywhere -- and that is why I prefer dealing with numbers rather than strings (if I have a choice, of course).
Well, your code fails with 1.998 (prints 2.0).
Re: floating point number formatting question
Quote:
Originally Posted by
VladimirF
Well, your code fails with 1.998 (prints 2.0).
Thanks for pointing it out. This made me find a simpler solution. Check this out:
Code:
#include <Math.h>
void print_custom_float(double fValue)
{
int nValueMult100 = (int)(fabs(fValue - (__int64)fValue) * 100.0 + 0.5);
if(nValueMult100 % 10)
{
_tprintf(_T("%.2f"), fValue);
}
else if(nValueMult100 % 100)
{
_tprintf(_T("%.1f"), fValue);
}
else
{
_tprintf(_T("%.0f"), fValue);
}
}
Re: floating point number formatting question
Quote:
Originally Posted by
ahmd
This made me find a simpler solution. Check this out...
Or like that:
Code:
void print_custom_float(double fValue)
{
int nValueMult100 = (int)(fabs(fValue - (__int64)fValue) * 100.0 + 0.5);
int n = (nValueMult100 % 10) ? 2 : (nValueMult100 % 100) ? 1 : 0;
_tprintf(_T("%.*f\n"), n, fValue);
}
Re: floating point number formatting question
Very nice! I didn't know that * is a special symbol. You can probably shrink it down to even 2 lines of code if you consolidate the 'n' variable. And we can probably save some on CPU cycles if we replace second division (nValueMult100 % 100) with (nValueMult100 < 100).
Re: floating point number formatting question
You might be able to gain control and flexibility if you print it as two integers:
Code:
#include <math.h>
void NoTrailingZerosPrintf(double const dValue, unsigned short nDecimalPlaces)
{
int const nFactor = static_cast<int>(pow(10.0, nDecimalPlaces));
int const iFixed = static_cast<int>(dValue * nFactor + (dValue > 0 ? 0.5 : -0.5));
int iFraction = abs(iFixed % nFactor);
while (iFraction > 0 && iFraction % 10 == 0) { iFraction /= 10; --nDecimalPlaces; }
printf (iFraction == 0 ? "%d\n" : "%d.%0*d\n", iFixed / nFactor, nDecimalPlaces, iFraction);
}
int main(int argc,char* argv[])
{
NoTrailingZerosPrintf(1.0000 , 2); // Test case 1.1
NoTrailingZerosPrintf(1.30 , 2); // Test case 1.2
NoTrailingZerosPrintf(1.32 , 2); // Test case 1.3
NoTrailingZerosPrintf(1.38 , 2); // Test case 1.4
NoTrailingZerosPrintf(1.322 , 2); // Test case 1.5
NoTrailingZerosPrintf(1.325 , 2); // Test case 1.6
NoTrailingZerosPrintf(-37.09 , 3); // Test case 2.1
NoTrailingZerosPrintf(-37.09 , 1); // Test case 2.2
NoTrailingZerosPrintf(-37.00005, 4); // Test case 2.3
NoTrailingZerosPrintf(-37.00004, 4); // Test case 2.4
NoTrailingZerosPrintf(-37.00005, 5); // Test case 2.5
NoTrailingZerosPrintf(1.998 , 2); // Test case 3.1
return 0;
}
Output:
Quote:
1
1.3
1.32
1.38
1.32
1.33
-37.09
-37.1
-37.0001
-37
-37.00005
2