Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a better way to round float number with n decimals in C++?

Tags:

c++

I known the common way is to multiply 10^n and then divide 10^n. round a float with one digit number But due to the double precision accuracy problem, I found the solution above doesn't fully work for my case:

0.965 * 0.9 = 0.8685

std::round(0.8685 * 1000) / 1000 = 0.869
// expects it to be 0.869
std::round(0.965 * 0.9 * 1000) / 1000 = 0.868

If I want to get 0.869 from std::round(0.965 * 0.9 * 1000) / 1000 directly, I have to change the statement to

std::round((0.965 * 0.9 + std::numeric_limits<double>::epsilon()) * 1000) / 1000 = 0.869

Is there a simpler way to do the rounding without add an epsilon for every calculation?

Edit: The problem is that, intuitively the value of std::round(0.965 * 0.9 * 1000) / 1000 shall be 0.869 (because 0.965 * 0.9 = 0.8685), but it actually give 0.868. I want to find common way get the accurate math value with 3 decimal precision.

like image 572
qduyang Avatar asked Nov 16 '17 07:11

qduyang


1 Answers

This is actually a surprisingly non-trivial problem, unless your problem is merely concerned with formatting output, in which case you can use an appropriate printf-style formatter, or ostream manipulator.

Fundamentally, denary rounding using a binary type doesn't make much sense. Your specific problem is due to the closest IEEE754 double to 0.965 being

0.96499999999999996891375531049561686813831329345703125

Your first port of call is to study Is floating point math broken?. Hopefully that will convince you that adding an arbitrary "epsilon" merely shifts the problem to other inputs, and there is no numerical justification for your using std::numeric_limits<double>::epsilon() either.

std::round works perfectly, due in part that the cutoff point x.5 for integer x is a dyadic rational so can be represented exactly in binary floating point. But alas, you can't use that to round to an arbitrary decimal point.

If you're willing to live with the occasional bad result then the std::round(x * y) / y idiom that you're currently using is probably your best bet.

If you can't live with any spurious errors, then you probably need to use a decimal type and perform the rounding functions on that. See C++ decimal data types

like image 97
Bathsheba Avatar answered Nov 08 '22 23:11

Bathsheba