Precision print doubles manipulator
Hello!
I've just created a stream manipulator that "precision formats" doubles. I
wanted a way to write doubles so that the number of decimals is of a
minimum precision. For example, I want all doubles to be written with up to
6 digits, if necessary, but less digits if possible. So the value 5 is
written as 5 and 0.9999999 as 1. Let me demonstrate with some examples that
write doubles with up to 6 decimals:
std::cout << precision_fmt<6>(5.) << std::endl;
std::cout << precision_fmt<6>(40.873918842465351) << std::endl;
std::cout << precision_fmt<6>(19025.898942144981) << std::endl;
std::cout << precision_fmt<6>(-100.00000002082101) << std::endl;
std::cout << precision_fmt<6>(44.705924135090932) << std::endl;
std::cout << precision_fmt<6>(-300.00000161733300) << std::endl;
std::cout << precision_fmt<6>(19126.088206883647) << std::endl;
std::cout << precision_fmt<6>(19154.942990107313) << std::endl;
std::cout << precision_fmt<6>(-299.99999975125598) << std::endl;
std::cout << precision_fmt<6>(299.99999826326098) << std::endl;
std::cout << precision_fmt<6>(149.99999990337500) << std::endl;
std::cout << precision_fmt<6>(-300.00000184236200) << std::endl;
std::cout << precision_fmt<6>(0.0045) << std::endl;
precision_fmt is a template where the parameter indicates the desired
precision.
This outputs the following:
5
40.873919
19025.898942
-100
44.705924
-300.000002
19126.088207
19154.94299
-300
299.999998
150
-300.000002
0.0045
One place where this is useful is when dealing with Xml. I think it is nice
to limit the size of the output.
Now to the code :-) I have used Angelika Langer's method of creating
manipulators.
template <class Manip>
class manip_base
{
public:
template <class Stream>
Stream& manipulate(Stream& str) const
{
return static_cast<const Manip&>(*this).fct(str);
}
};
template <class Ostream, class Manip>
Ostream& operator<< (Ostream& os, const manip_base<Manip>& m)
{
return m.manipulate(os);
}
template<std::streamsize N>
struct precision_fmt : public manip_base<precision_fmt>
{
double m_d;
precision_fmt(double d)
: m_d(d)
{}
static double RoundDouble(double doValue, std::size_t nPrecision)
{
static const double doBase = 10.0;
double doComplete5, doComplete5i;
doComplete5 = doValue * pow(doBase, (double) (nPrecision + 1));
if(doValue < 0.0)
doComplete5 -= 5.0;
else
doComplete5 += 5.0;
doComplete5 /= doBase;
modf(doComplete5, &doComplete5i);
return doComplete5i / pow(doBase, (double) nPrecision);
}
template<class Stream>
Stream& fct(Stream& stream) const
{
struct saveFlags
{
Stream& m_stream;
std::ios_base::fmtflags m_flags;
saveFlags(Stream& stream)
: m_stream(stream),
m_flags(stream.flags())
{}
~saveFlags()
{
m_stream.flags(m_flags);
}
} saveFlags(stream);
std::streamsize decimals = 0;
for( std::streamsize i=N; i>0; i-- )
{
if( RoundDouble(m_d, i-1)!=RoundDouble(m_d, i) )
{
decimals = i;
break;
}
}
stream.setf(std::ios_base::fixed);
stream.precision(decimals);
stream << m_d;
return stream;
}
};
Hopefully somebody will also find this useful. Any comments?
--
Daniel