8

Can I specify setprecision to round double values when stream to std output?

ofile << std::setprecision(12) << total_run_time/TIME << "\n";

Output: 0.756247615801

ofile << std::setprecision(6)<< total_run_time/TIME << "\n";

Output: 0.756248

But I need the output as 0.756247

Thanks

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
brsbilgic
  • 11,613
  • 16
  • 64
  • 94

4 Answers4

17

There is also std::fesetround from <cfenv>, which sets the rounding direction:

#include <iostream>
#include <iomanip>
#include <cmath>
#include <cfenv>

int main () {
    double runtime = 0.756247615801;

    // Set rounding direction and output with some precision:
    const auto prev_round = std::fegetround();
    std::fesetround(FE_DOWNWARD);    
    std::cout << "desired: " << std::setprecision(6) << runtime << "\n";

    // Restore previous rounding direction and output for testing:
    std::fesetround(prev_round);
    std::cout << "default: " << std::setprecision(6) << runtime << "\n";
}

(note that these are not the kind of comments I recommend, they are just for tutoring purposes)

Output:

desired: 0.756247
default: 0.756248

Important note, though: I did not find any mention in the standard, that the operator<< overloads for floating types have to honour the rounding direction.

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
  • The only 100% correct solution so far, assuming a quality implementation where the functions to convert to decimal honor the rounding mode. – Pascal Cuoq Nov 28 '13 at 19:11
  • @PascalCuoq: I added the part named `Important note, though` – Sebastian Mach Dec 05 '13 at 08:17
  • @phreshel `fesetround` returns "Zero, if the requested rounding direction was successfully set. Otherwise, a non-zero value": http://www.cplusplus.com/reference/cfenv/fesetround/#return I think you wanted to use: [`fegetround`](http://www.cplusplus.com/reference/cfenv/fegetround/) I took the liberty of editing your answer. Feel free to rollback if my edit doesn't make sense. – Jonathan Mee Dec 06 '14 at 15:08
  • @JonathanMee: Whoops. I am perfectly okay with your perfectly valid edit. Thanks! – Sebastian Mach Dec 08 '14 at 11:22
2

Multiply the result of your division by a million, convert to an integer, and divide by a million (as a double). Have the side-effect that std::setprecision is not needed for the output.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 3
    +1 but, it obviously will not affect you in this instance, but always take care with this approach not to overflow your integer. – Bathsheba Nov 28 '13 at 11:22
  • This will overflow only after 35.79 minutes (more precisely, if your integer is 32bit, you enter undefined behaviour (in practice most probably overflow) for total_run_time>=2147.5.) – Sebastian Mach Nov 28 '13 at 11:33
  • 1
    Avoid the conversion to `int` (and the resulting possible overflow) by using `modf` (which breaks a `double` into its integral and fractional parts). – James Kanze Nov 28 '13 at 11:38
  • 1
    edit: some dirty trial and error bisection reveals an event horizon around 2147.483648000000130125 – Sebastian Mach Nov 28 '13 at 11:39
  • @phresnel I didn't mean the OP to multiply the time, but the result of the division. Clarified the answer. Oh, and of course the value you came up with would be `INT_MAX` divided by a million (and some rounding error). – Some programmer dude Nov 28 '13 at 11:46
  • @JoachimPileborg: Yes, the rounding part was what I wanted to exercise :) I upvote the new clarified variant – Sebastian Mach Nov 28 '13 at 11:55
  • 2
    There is no need to convert to integer; the floor or trunc functions will provide the desired result without conversion. – Eric Postpischil Nov 28 '13 at 12:00
  • 1
    Multiplying by 1000000 produces a rounded result, which can lose the desired rounded-down value. – Eric Postpischil Nov 28 '13 at 12:01
2

Another approach is to defeat the rounding by subtracting, in your second case, 0.000005 from the double before outputting it:

total_run_time / TIME - 0.000005

In many ways I prefer this as it avoids the potential for integer overflow.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
1
std::cout.write(std::to_string(0.756247615801).c_str(), 8);

It looks really dirty, but it works!

  • you could make it better and use `std::string::substring(0,9)` – Sebastian Mach Nov 28 '13 at 11:44
  • std::string::substr creates an additional string object which is not needed, so I just used write to output the relevant part. –  Nov 28 '13 at 11:49
  • I just thought it would have better self-documentation, but you're right. – Sebastian Mach Nov 28 '13 at 11:56
  • This solution has a double-rounding problem. The fewer additional digits are used in the initial conversion to decimal, the more noticeable the double-rounding problem is. There exists a number of digits for the initial conversion that means double-rounding is innocuous for all `double`s but I am not sure how many that is. This has something to do with the largest number of consecutive non-leading non-trailing zeroes there can be in the exact representation of a `double` in decimal. – Pascal Cuoq Nov 28 '13 at 19:09
  • This approach works in all cases, assuming a correctly rounded conversion to decimal, if 21 additional digits are printed. Ref: http://stackoverflow.com/a/20302431/139746 – Pascal Cuoq Nov 30 '13 at 16:45