10

What is the most elegant way to output a floating point number in C++ with no scientific notation or trailing zeros?

float a = 0.000001f;
float b = 0.1f;

cout << "a: " << a << endl;     //  1e-006 terrible, don't want sci notation.
cout << "b: " << b << endl;     //  0.1 ok.

cout << fixed << setprecision(6);
cout << "a: " << a << endl;     //  0.000001 ok.
cout << "b: " << b << endl;     //  0.100000 terrible, don't want trailing zeros.
Neutrino
  • 8,496
  • 4
  • 57
  • 83
  • This question addresses the trailing zeros, does it help? http://stackoverflow.com/questions/11047994/do-not-output-trailing-zeroes-in-c – zero298 Sep 18 '13 at 20:48
  • It certain addresses the same issue, but I was hoping for a less manual solution than outputting to a string and then manually replacing trailing zeros. – Neutrino Sep 19 '13 at 08:46
  • @Neutrino : It is not a "manual" solution, its code. If it were supported by the library do you think it would do it differently? It is already generating a string, and it would have to generate all the digits to determine the least significant digit. You write it once and thereafter it is no different that using library code. – Clifford Sep 21 '13 at 07:26
  • [double to string without scientific notation or trailing zeros, efficiently](https://stackoverflow.com/q/15165502/995714) – phuclv May 29 '18 at 01:16

3 Answers3

6

I am not sure about the "most elegant way" but here's one way.

#include <iostream>
#include <iomanip>
#include <sstream>

using namespace std ;
string fix( float x, int p )
{
    ostringstream strout ;
    strout << fixed << setprecision(p) << x ;
    string str = strout.str() ;
    size_t end = str.find_last_not_of( '0' ) + 1 ;
    return str.erase( end ) ;
}


int main()
{
    float a = 0.000001f ;
    float b = 0.1f ;

    cout << "a: " << fix( a, 6 ) << endl;     //  0.000001 ok.
    cout << "b: " << fix( b, 6 ) << endl;     //  0.1 ok.

   return 0;
}

You could perhaps create your own I/O manipulator if you need to to a lot of this kind of output. That is arguably more elegant, but the implementation could be similar.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • Is there no way to set the configuration of the stream itself such that the floats get output in the desired format without having to convert or otherwise manipulate each individual insertion? – Neutrino Sep 19 '13 at 09:11
  • @Neutrino: If I knew of one, I'd have posted it. Does it matter? You write that function once then use it everywhere. – Clifford Sep 19 '13 at 19:11
  • Well setting the formatting configuration of the stream, which the stream then remembers and uses for all subsequent insertions of the same type is kind of the whole idea behind the stream formatting design. Having to call the same conversion function over and over is obviously not in keeping with that design and is consequently nowhere near as elegant. – Neutrino Sep 20 '13 at 08:43
  • @Neutrino: Modifying the state of the stream and adding your own state flags would require access to its private members. I think your only option would be to create a class that inherits ostream and overrides << for float. That seems rather too much trouble for this simple requirement. You could create an alias for `float` - `typedef float ffloat`, then overload << for the type `ffloat`, which would then behave as a plain `float` everywhere except on `ostream::operator<<`. – Clifford Sep 21 '13 at 07:21
  • A typedef doesn't create a new type, just a synonym for an existing type. Consequently you can't create a function overload for a typedef where there is already an existing function for the type the typedef refers to. The compiler will flag it as ambiguous. There are libraries out there that purport to provide 'true typedef' functionality, so that could provide the basis of an implemention along the line your suggest. It is a rather heavy-weight approach though. Quite possibly the answer is that there is no elegant way to do this in C++. – Neutrino Dec 15 '16 at 11:36
  • @Neutrino : I guess it was not that critical if it took three years to discover my error! You are right. I believed erroneously that the overloading resolution took account of type names even for typedef aliases - not sure what gave me that impression. Personally I prefer my original solution in any case; the "persistent" stream flags are mostly a nuisance in my experience. – Clifford Dec 15 '16 at 13:42
2

If string manipulating doesn't hurt your eyes:

std::string fixedfloat(float x)
{
    std::ostringstream ss;
    ss << std::fixed << std::setprecision(std::cout.precision()) << x;
    std::string str = ss.str();
    return str.substr(0, str.find_last_not_of('0') + 1);
}

int main()
{
    float b = 0.1f;

    std::cout << std::setprecision(6) << fixedfloat(b);
}

or

class fixedfloat
{
public:
    fixedfloat(float x) : x(x) {}
    float value() const { return x; }

private:
    float x;
};

ostream &operator<<(ostream &out, const fixedfloat &f)
{
    ostringstream ss;
    ss << fixed << setprecision(out.precision()) << f.value();
    string str = ss.str();
    out << str.substr(0, str.find_last_not_of('0') + 1);
    return out;
}

int main()
{
    float b = 0.1f;

    cout << setprecision(6) << fixedfloat(b);
}
masoud
  • 55,379
  • 16
  • 141
  • 208
  • The first approach assumes cout is the final destination and uses that to match the required precision. The second approach is better but requires every insertion to the stream to be explicitly manipulated. Is there no way to set the configuration of the stream itself such that the floats get output in the desired format without having to convert or otherwise manipulate each individual insertion? – Neutrino Sep 19 '13 at 09:10
  • 1
    This leaves a trailing dot. – Niko O Feb 10 '22 at 14:20
0

the other example like mine actually output "200." or did "200" >> "2".

this should work for everything (as I took it from a string to val function I use).

string fix(float in) {       
    string s = to_string(in);
    size_t dot = s.find_first_of('.'), last = s.find_last_not_of(".0");

    if (dot!=string::npos) return s.substr(0, max(dot,last+1));
    else return s;
}
Smellymoo
  • 97
  • 7