Search code examples
c++floating-pointformattingc++17cout

std::cout print all digits of float value


I have this function:

template<typename T> // T can be float, double or long double
void printAllDigits(T value)
{
    std::cout << std::fixed << std::setprecision(999) << value;
}

It's a dumb implementation to print all digits of a floating point value.

This has some problems:

  • I can't guarantee that it works for all float types. It seems to work for float (999 digits is probably enough), and maybe works for double, but certainly does not work for long double (std::numeric_limits<long double>::min() is printed as "0.", followed by 999 zeros).
  • It is extremely wasteful, because e.g. for float it always prints a ton of trailing zeros, even though those can never be non-zero.

But it does some things right:

  • I never want scientific notation, so std::fixed makes sense.
  • I have code that strips the trailing zeros (akin to what is sugessted in Remove trailing zero in C++), which works well with this approach.
  • I don't have to write down separate code paths or constants for the different float types.
  • If the argument to setprecision is large enough, this actually prints all digits without rounding.
  • I can copy the output, plonk it back into a code file (and make sure to add ".0f" and such where necessary) and get the same floating point value.
  • "Unnecessary" digits are not rounded away. printAllDigits(0.1f) prints "0.100000001490116119384765625000000...". Printing "0.1" would be sufficient to get back to the original float value, but I still need the function to print all of those digits.

How can I make that function less wasteful while maintaining my requirements?

std::numeric_limits<T>::max_digits10 is incorrect, since it is too small! std::numeric_limits<float>::min() gets printed as "0.000000000" instead of "0.0000000000000000000000000000000000000117549435082228750796873653722224567781866555677208752150875170627841725945472717285156250000000..."


Solution

  • Assuming that the implementation uses radix 2 for the floating point,
    if (std::numeric_limits<T>::radix == 2)
    then writing ALL the decimal digits for ALL possible values would require:

    std::cout << std::setprecision(std::numeric_limits<T>::digits - std::numeric_limits<T>::min_exponent);
    

    I suspect that the formulation would be the same for radix == 10, but I cannot check this (no implementation at hand).

    I wonder why you want to print all the decimals. If it's just to reconstruct the value unchanged, this is not necessary. Using a mixture of scientific notation with a precision std::numeric_limits<T>::max_digits10 should do the job. Otherwise, there are well known algorithm to print just enough decimal digits. They are used in the main REPL languages (see python repr, or how java, javascript, some Smalltalk, etc... print the floating point values), unfortunately, they are not part of a standard C++ library AFAIK.