I often need to display or record output of floating-point values, in a human-readable decimal format. This is easy enough in most programming languages… unless you need the representation to be one-to-one, so that distinct finite values always produce distinct output. More precisely, we would like to be able to (1) convert a floating-point value to a string, then (2) convert the string back to floating-point, recovering exactly the original value
.
For example, I use MATLAB more than I care to, and despite its primary application being numeric scientific computing, none of its built-in output formats support this. The simplest way that I know of to do this is actually via MATLAB’s interface to Java:
>> java.lang.Double.toString(1.1 + 2.2 - 3.3)
In C++, you can set the precision of an output stream to a desired number of decimal digits. In C++11, the constant max_digits10 is defined just for this purpose:
#include <sstream> // ostringstream
#include <iomanip> // setprecision
#include <limits> // numeric_limits
template<typename T>
std::string to_string(T x)
{
std::ostringstream os;
os << std::setprecision(std::numeric_limits<T>::max_digits10) << x;
return os.str();
}
Unfortunately, I learned the hard way that Visual Studio 2010 gets this wrong, at least for single-precision float, defining max_digits10 in that case to be 8 instead of 9 as it should be.
For compatibility with older compilers– either with bugs like VS 2010, or simply pre-C++11– the good news is that we can compute the required number of digits , as a function of the number of significant bits
in the underlying floating-point representation (which is in turn defined in the standard header <limits> as numeric_limits<T>::digits):
where for float and
for double, yielding
and
, respectively. (Since I can’t seem to write a post without including an obligatory puzzle: why is the +1 needed in the above equation?)
Interestingly, the following alternative formula also works, with the added benefit that it can be evaluated at compile time:
max_digits10 = std::numeric_limits<T>::digits * 3010 / 10000;
I’m not sure why this particular approximation of is used, since 301/1000 works equally well; both are correct for any number of bits less than 196.
(Edit: The last time I wrote about floating-point issues, I mentioned Bruce Dawson’s Random ASCII blog, in particular the excellent series of articles about floating point. I should have done so here as well. In this case, the specific relevant article is here… but instead of the brute force enumeration approach described there explaining the “+1” in the formula above– and how often it is actually needed– my motivation for this post, in the form of the “puzzle,” was to suggest a pencil-and-paper derivation of that formula, which would in turn yield a direct calculation of how often that +1 is needed.)