Re: Precision print doubles manipulator
Daniel Lidstr?m wrote:
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;
In an ideal world, I'd expect to write:
std::cout << precision_fmt(6) << 40.873918842465351 << std::endl;
though I think that would be impossible to implement without writing
your own num_put locale facets. More comments below.
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);
}
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;
}
}
I'm not convinced that the above is guaranteed to work - it makes
assuptions about floating point calculations that I don't think are
guaranteed, and which might break down in the face of an aggressive
optimizer. In particular I think your code assumes something like
x / y == (x * 10) / (y * 10)
which I don't think necessarily holds for floating point numbers,
particularly when you throw in pow calculations. You're probably ok on
IEEE compliant machines.
stream.setf(std::ios_base::fixed);
stream.precision(decimals);
stream << m_d;
return stream;
}
};
Hopefully somebody will also find this useful. Any comments?
I'd use a different algorithm for determining the precision. The
simplest way is to convert to a string using the max precision (N), and
then lop off the all trailing 0s before outputting to the stream.
Another way would be to compare your two RoundDouble results using an
epsilon calculated from i, such as pow(10, -(i + 1)) or something (I
can't think exactly what you need here).
Tom