I need to find the floor nth root of a given number(x). x
can be as large as 1e12
and n
can be as large as 50. floor(pow(64,1.0/3))
returns 3
, please help me with this problem, if not can you please suggest an alternative taking the keeping in mind?
Edit: I know its about floating point precision, I am asking what the alternative should be in such situations.
Even the following code returns True
double x = pow(64,1.0/3);
return x==(int)x;

- 13
- 5
-
Because the floor of 3.999999999 is 3 and not 4. Look at the result as a decimal not an integer. – Gordon Linoff Dec 05 '16 at 13:00
-
And `1.0/3` is not exactly one third. Floating point is just an approximation of the real numbers. – molbdnilo Dec 05 '16 at 13:03
-
And thats why I asked for an alternative :/ – Istasis Mishra Dec 05 '16 at 13:09
-
1The "alternative" is to realize what floating point values are (binary representation of decimal real numbers), thus they are approximations, as already stated. It's up to you to recognize this and rearrange your calculations accordingly. – PaulMcKenzie Dec 05 '16 at 13:24
-
What exactly is the underlying resp. surrounding problem? Why do you exclude to use `round(pow(64,1.0/3))` to get the closest integer to the result? – Lutz Lehmann Dec 05 '16 at 14:24
-
I need the floor specifically. Its actually a problem in competitive programming. – Istasis Mishra Dec 05 '16 at 14:38
-
Then use `floor(pow(64,1.0/3)+1e-15)` to skip over the noise in the last bits of the mantissa. – Lutz Lehmann Dec 05 '16 at 14:53
-
@IstasisMishra If it is a competitive programming is every other competitor using C++, or some other language that uses fixed floating point or arbitrary precision types? If so, then you're at a disadvantage using C++, as those types do not natively exist. You would either have to write your own library, or use the available ones such as [boost::multiprecision](http://www.boost.org/doc/libs/1_62_0/libs/multiprecision/doc/html/boost_multiprecision/tut/floats.html) – PaulMcKenzie Dec 05 '16 at 18:40
2 Answers
You are dealing with good old floating-point imprecision. See What is the numerical stability of std::pow() compared to iterated multiplication?, and especially Pascal Cuoq's answer, for an in-depth explanation why the result of std::pow
in particular will be imprecise. Because of rounding errors, you will occasionally get a result that is ever so slightly less than 4, and so std::floor
will round down to 3.
The answer I linked above says:
A quality implementation of
pow
will give you 1 ULP of accuracy for its result, and the best implementations will “guarantee” 0.5 ULP.
ULP here refers to the Unit of Least Precision or Unit in the Last Place. Knowing about this error, you can increase the std::pow()
result before calling std::floor
. A good way to do this is using std::nextafter
, which gives you the next-larger representable floating-point value (i.e., 1 ULP up). I think that if Pascal's statement on the precision of std::pow()
holds, calling nextafter
once should put you back above 4, in your particular example. Here's the code I recommend:
template <typename T>
T floorroot2(T x, T e)
{
const auto r = std::pow(x, T(1.0)/e);
return std::floor(std::nextafter(r, r+1));
}
That works for me (live example), but if you want to be extra sure if you don't trust your library's pow
implementation, you may want to add 2 ULPs, i.e. call nextafter(nextafter(r, r+1), r+1)
.
#include <iostream>
#include <cmath>
using namespace std;
template<class Type>
Type compute(Type v1, Type v2, Type v3)
{
return std::floor(std::pow(v1, v2 / v3));
}
int main() {
std::cout << compute<float>(64, 1, 3) << std::endl;
std::cout << compute<double>(64, 1, 3) << std::endl;
std::cout << compute<long double>(64, 1, 3) << std::endl;
}
expected output:
4
3
4

- 68,278
- 7
- 90
- 142