The thing with floating points is that they get more innacurate the greater or smaller they are. For example:
double x1 = 10;
double x2 = 20;
std::cout << std::boolalpha << (x1 == x2);
prints, as expected, false
.
However, the following code:
// the greatest number representable as double. #include <limits>
double x1 = std::numeric_limits<double>::max();
double x2 = x1 - 10;
std::cout << std::boolalpha << (x1 == x2);
prints, unexpectedly, true
, since the numbers are so big that you can't meaingfully represent x1 - 10
. It gets rounded to x1
.
One may then ask where and what are the bounds. As we see the inconsistencies, we obvioulsy need some tools to inspect them. <limits>
and <cmath>
are your friends.
std::nextafter
:
std::nextafter
takes two float
s or double
s. The first argument is our starting point and the second one represents the direction where we want to compute the next, representable value. For example, we can see that:
double x1 = 10;
double x2 = std::nextafter(x1, std::numeric_limits<double>::max());
std::cout << std::setprecision(std::numeric_limits<double>::digits) << x2;
x2
is slightly more than 10
. On the other hand:
double x1 = std::numeric_limits<double>::max();
double x2 = std::nextafter(x1, std::numeric_limits<double>::lowest());
std::cout << std::setprecision(std::numeric_limits<double>::digits)
<< x1 << '\n' << x2;
Outputs on my machine:
1.79769313486231570814527423731704356798070567525845e+308
1.7976931348623155085612432838450624023434343715745934e+308
^ difference
This is only 16th decimal place. Considering that this number is multiplied by 10308, you can see why dividing 10
changed absolutely nothing.
It's tough to talk about specific values. One may estimate that double
s have 15 digits of precision (combined before and after dot) and it's a decent estimation, however, if you want to be sure, use convenient tools designed for this specific task.