11

When dealing with floating point values in Java, calling the toString() method gives a printed value that has the correct number of floating point significant figures. However, in C++, printing a float via stringstream will round the value after 5 or less digits. Is there a way to "pretty print" a float in C++ to the (assumed) correct number of significant figures?


EDIT: I think I am being misunderstood. I want the output to be of dynamic length, not a fixed precision. I am familiar with setprecision. If you look at the java source for Double, it calculates the number of significant digits somehow, and I would really like to understand how it works and/or how feasible it is to replicate this easily in C++.

/*
 * FIRST IMPORTANT CONSTRUCTOR: DOUBLE
 */
public FloatingDecimal( double d )
{
    long    dBits = Double.doubleToLongBits( d );
    long    fractBits;
    int     binExp;
    int     nSignificantBits;

    // discover and delete sign
    if ( (dBits&signMask) != 0 ){
        isNegative = true;
        dBits ^= signMask;
    } else {
        isNegative = false;
    }
    // Begin to unpack
    // Discover obvious special cases of NaN and Infinity.
    binExp = (int)( (dBits&expMask) >> expShift );
    fractBits = dBits&fractMask;
    if ( binExp == (int)(expMask>>expShift) ) {
        isExceptional = true;
        if ( fractBits == 0L ){
            digits =  infinity;
        } else {
            digits = notANumber;
            isNegative = false; // NaN has no sign!
        }
        nDigits = digits.length;
        return;
    }
    isExceptional = false;
    // Finish unpacking
    // Normalize denormalized numbers.
    // Insert assumed high-order bit for normalized numbers.
    // Subtract exponent bias.
    if ( binExp == 0 ){
        if ( fractBits == 0L ){
            // not a denorm, just a 0!
            decExponent = 0;
            digits = zero;
            nDigits = 1;
            return;
        }
        while ( (fractBits&fractHOB) == 0L ){
            fractBits <<= 1;
            binExp -= 1;
        }
        nSignificantBits = expShift + binExp +1; // recall binExp is  - shift count.
        binExp += 1;
    } else {
        fractBits |= fractHOB;
        nSignificantBits = expShift+1;
    }
    binExp -= expBias;
    // call the routine that actually does all the hard work.
    dtoa( binExp, fractBits, nSignificantBits );
}

After this function, it calls dtoa( binExp, fractBits, nSignificantBits ); which handles a bunch of cases - this is from OpenJDK6


For more clarity, an example: Java:

double test1 = 1.2593;
double test2 = 0.004963;
double test3 = 1.55558742563;
    
System.out.println(test1);
System.out.println(test2);
System.out.println(test3);

Output:

1.2593
0.004963
1.55558742563

C++:

std::cout << test1 << "\n";
std::cout << test2 << "\n";
std::cout << test3 << "\n";

Output:

1.2593
0.004963
1.55559
vitaut
  • 49,672
  • 25
  • 199
  • 336
dave
  • 1,607
  • 3
  • 16
  • 20
  • 3
    The `toString` method does not give the "correct number" of significant figures; it has no way of knowing which of the figures are significant. IEEE floating-point numbers don't represent that information. – Rob Kennedy Jul 27 '12 at 20:41
  • For reference, here is a common Java approach to [*Customizing Formats*](http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html). – trashgod Jul 27 '12 at 20:44
  • Java does do a series of calculations on the double value when printing - I am just not knowledgeable enough to understand what it is doing. See edit – dave Jul 27 '12 at 21:26
  • 1
    @Dave Yes you're being misunderstood, one problem with SO is that people can be eager to get their answer in first, sometimes at the expense of understanding what's being asked. I've been guilty of this myself. – jahhaj Jul 27 '12 at 22:05

4 Answers4

6

I think you are talking about how to print the minimum number of floating point digits that allow you to read the exact same floating point number back. This paper is a good introduction to this tricky problem.

http://grouper.ieee.org/groups/754/email/pdfq3pavhBfih.pdf

The dtoa function looks like David Gay's work, you can find the source here http://www.netlib.org/fp/dtoa.c (although this is C not Java).

Gay also wrote a paper about his method. I don't have a link but it's referenced in the above paper so you can probably google it.

jahhaj
  • 3,021
  • 1
  • 15
  • 8
  • Thanks! I feel like you were the only person to get what I was talking about. – dave Jul 27 '12 at 23:13
  • The link to the paper doesn't seem to work anymore. Wonder if it's "How to Read Floating Point Numbers Accurately" by William D Clinger. – M. M. Nov 22 '18 at 19:19
2

Is there a way to "pretty print" a float in C++ to the (assumed) correct number of significant figures?

Yes, you can do it with C++20 std::format, for example:

double test1 = 1.2593;
double test2 = 0.004963;
double test3 = 1.55558742563;
std::cout << std::format("{}", test1) << "\n";
std::cout << std::format("{}", test2) << "\n";
std::cout << std::format("{}", test3) << "\n";

prints

1.2593
0.004963
1.55558742563

The default format will give you the shortest decimal representation with a round-trip guarantee like in Java.

Since this is a new feature and may not be supported by some standard libraries yet, you can use the {fmt} library, std::format is based on. {fmt} also provides the print function that makes this even easier and more efficient (godbolt):

fmt::print("{}", 1.2593);

Disclaimer: I'm the author of {fmt} and C++20 std::format.

vitaut
  • 49,672
  • 25
  • 199
  • 336
-1

You can use the ios_base::precision technique where you can specify the number of digits you want

For example

#include <iostream>
using namespace std;

int main () {
double f = 3.14159;
cout.unsetf(ios::floatfield);            // floatfield not set
cout.precision(5);
cout << f << endl;
cout.precision(10);
cout << f << endl;
cout.setf(ios::fixed,ios::floatfield);   // floatfield set to fixed
cout << f << endl;
return 0;

The above code with output
3.1416
3.14159
3.1415900000

Maverick
  • 91
  • 3
  • 10
  • What I am trying to avoid is the trailing zeros in your example. I do not want a fixed value for my output. – dave Jul 27 '12 at 21:31
-1

There is a utility called numeric_limits:

#include <limits>

    ...
    int num10 = std::numeric_limits<double>::digits10;
    int max_num10 = std::numeric_limits<double>::max_digits10;

Note that IEEE numbers are not represented exactly bydecimal digits. These are binary quantities. A more accurate number is the number of binary bits:

    int bits = std::numeric_limits<double>::digits;

To pretty print all the significant digits use setprecision with this:

out.setprecision(std::numeric_limits<double>::digits10);
emsr
  • 15,539
  • 6
  • 49
  • 62
  • this is not what i am looking for – dave Jul 27 '12 at 23:12
  • OK, I get it I think. You want numbers that track the precision from construction through numeric operations? C++ standard doesn't have this. There are efforts to add multi-precision numbers. I think your requirement is reasonable for such numbers. Basically, fundamental floating point numbers just don't have the smarts for this and never will. – emsr Jul 28 '12 at 03:46
  • Check this out: http://www.holoborodko.com/pavel/mpfr/. I think this is what you want. Good luck. – emsr Jul 28 '12 at 03:49