0

I have noticed something very odd when using std::floor. I'm am compiling C++ on ubuntu. I'm trying to floor a double to the nearest hundredth and I'm checking the cases where the input value is already rounded to the nearest hundredth and I'm getting inconsistent results.

My code:

double hundredthFloor(double value){
  double hundredth = 0.01;
  return hundredth * std::floor( value / hundredth);
}

In most cases it works,

value = 99.87 gives output 99.87

value = 99.879 gives output 99.87

value = 0.39 gives output 0.39

However:

value = 0.29 gives value 0.28

value = 0.47 gives value 0.46

I've tried many input values and so far only 0.29 and 0.47 seem to output a value one decimal below what I'd expected.

Has anyone seen anything similar?

Dhanuka
  • 2,826
  • 5
  • 27
  • 38
Neo
  • 1

3 Answers3

1

The reason for what you see is that some of the values in your example, such as 0.01 and 0.29, cannot be represented exactly using float or double.

You can get a better view of what is going on if you print the result with many significant digits:

cout << fixed << setprecision(15) << (value / hundredth) << endl;

When value is 0.29, this prints

28.999999999999996

This number is very close to 29, but floor truncates it to 28 anyway.

One way to address this is to add epsilon to the result of division:

return hundredth * std::floor( value / hundredth + numeric_limits<double>::epsilon());
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Thought I'd add: the program can probably be made to work more reliably by e.g. Adding 0.5 to the quotient before taking the floor. – Nir Friedman May 27 '15 at 14:32
0

What's your EPS value? I always use the EPS values to check first if my calculation is wrong or if it is the lack of precision of the computer:

http://www.cplusplus.com/reference/cfloat/

mmoment
  • 1,269
  • 14
  • 30
0

Try the following:

cout.precision(17);
cout << fixed << 0.29 << endl;
cout << hundredthFloor(0.29) << endl;

You will get something like:

0.28999999999999998
0.28000000000000003

This is because a double can't always represent a decimal number exactly, see this answer for details. This means that sometimes a number will be a little more or less than you expect; floor will not take this into consideration, and thus 0.29 will internally be represented as 0.28999999... and rounded down to 0.28.

A simple solution is to add a very small number, such as numeric_limits<double>::epsilon(), before calling std::floor:

double hundredthFloor(double value){
  double hundredth = 0.01;
  return hundredth * std::floor( value / hundredth + std::numeric_limits<double>::epsilon() );
}
Community
  • 1
  • 1
Frxstrem
  • 38,761
  • 9
  • 79
  • 119