31

Currently, I can round a double to an output stream using:

output.setf(std::ios::fixed,std::ios::floatfield);
output.precision(3);

But I'm given a double and I need to make the conversion before I insert it to a vector. So for instance, if the number -0.00078 appears then it equals to 0.000 and I won't need to save it. On the other hand, 1.0009 will become 1.001 (same as the precision function handles it).

How can I convert doubles like that in C++?

Melebius
  • 6,183
  • 4
  • 39
  • 52
Tom
  • 9,275
  • 25
  • 89
  • 147

4 Answers4

56

A common trick is to do it with math:

value = std::round(value * 1000.0) / 1000.0;

This scales the value, applies ordinary rounding and then scales it back. You may have some precision loss as is typical with floating-point math, and this won't be effective at extreme ranges of what can be represented by double, but often it's "good enough".

For something more generalized, put this in a function that accepts rounding to any amount:

double round_to(double value, double precision = 1.0)
{
    return std::round(value / precision) * precision;
}

Example:

#include <cmath>
#include <iostream>

double round_to(double value, double precision = 1.0)
{
    return std::round(value / precision) * precision;
}

int main()
{
    double value = 10.0078;
    double precision[] = { 0.001, 0.01, 0.1, 1, 2, 3, 4 };
    for (double p : precision)
    {
        double result = round_to(value, p);
        std::cout << "round_to(" << value << ", " << p << ") = " << result << "\n";
    }
}

Output:

round_to(10.0078, 0.001) = 10.008
round_to(10.0078, 0.01) = 10.01
round_to(10.0078, 0.1) = 10
round_to(10.0078, 1) = 10
round_to(10.0078, 2) = 10
round_to(10.0078, 3) = 9
round_to(10.0078, 4) = 12
paddy
  • 60,864
  • 6
  • 61
  • 103
  • 4
    There are already [a bunch of standard-library functions for rounding](http://en.cppreference.com/w/c/numeric/math/round), no? – Oliver Charlesworth Jan 16 '13 at 23:36
  • 1
    @OliverCharlesworth the std::round functions only round to the nearest integer. For decimal point, you will need to do the trick that paddy suggested. – Yun Sep 13 '16 at 17:52
  • 7
    @yun No, he means you don't need to define your own `round` function like I did there because it already exists. – paddy Sep 14 '16 at 02:44
  • `floor(val + 0.5)` fails many cases when `val + 0.5` is inexact. `round( value * 1000.0 ) / 1000.0` has similar troubles - for values near half-way. (when `value * 1000.0` is near `xxxxx.5`. – chux - Reinstate Monica Jul 04 '21 at 11:26
  • @GouthamM It seems you accidentally posted part of your potential answer as a comment on my answer, even though I use an entirely different approach. Not only did you do that here, but also in the comments on the original question. And yet, your suggestion of using string streams for this has [already been offered](https://stackoverflow.com/a/43722894/1553090) in the form of an answer five years ago, albeit using a different way of extracting the final value as a numeric type. If you think your approach improves on that, make a suggestion _there_ or post an actual answer of your own. – paddy Jun 15 '22 at 21:59
  • @paddy, yeah it was accidental. I initially commented here, after realizing I had commented on the question section but unfortunately didn't delete it here. DELETED now. sorry for bothering – GMudide Jun 17 '22 at 09:54
11

I know this is a very old post but I was looking for a solution to the same problem. However, I did not want to create a special function for it so I came up with the following:

#include <sstream>
#include <iomanip>
...
...
...
double val = 3.14159;
stringstream tmp;
tmp << setprecision(3) << fixed << val;
double new_val = stod(tmp.str());   // new_val = 3.143
tmp.str(string());                  // clear tmp for future use

Not sure if this is the best way to do it but it worked for me!

classyGrrrl
  • 111
  • 1
  • 2
  • 1
    This is an easy way to get the rounding done without having to write math expressions explicitly. However, wouldn't the two conversions between string and float make this less efficient that using a math formula? Of course, the performance overhead should be barely visible if the rounding is to be done for just a few numbers, not for tons of them. – so2 Feb 04 '21 at 18:14
  • This solution is great if your final output is a string (which is most likely if you're rounding in the first place). Thanks! – Ryan_DS Feb 09 '23 at 07:28
9

Other answers here have given you a technique. But it's important to mention that not all values can be exactly represented in floating-point. 1.001 is a good example; the nearest possible value is 1.00099999999999988987.

So if your aim is to get strictly 3 decimal places, then the answer is: that's not possible.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • Well, technically you can. You round the number to a whole and output that with no decimal places, then you multiply the original by 1000 and `fmod` by 1000 and output that as an integer with leading zeros. Oh yeah, and output the decimal point. – paddy Jan 16 '13 at 23:33
  • @paddy: Yes, that's true. If you're prepared to work with `1000*x` at all points through your calculations, then you absolutely can. (Perhaps you should put this in your answer.) – Oliver Charlesworth Jan 16 '13 at 23:35
5

You can multiply it by 1000 and then round (or truncate) it; this will give you a value 1000 times the 3-decimal place value. Note that, if you divide it by 1000 to get the 'rounded' value, you may end up w/ more than 3 decimal places (due to round off error).

Scott Hunter
  • 48,888
  • 12
  • 60
  • 101
  • I think the round off error due to /1000 is important to take into account, indeed. – mfnx Oct 17 '20 at 17:41