I am creating a problem which requires me to find the cube root of certain numbers, some of them have whole number roots, but a lot of them don\'t.
I have numbers li
We first calculate a candidate integer of the cubit root by a very rough rounding (int(... + 0.1)
) then verify if it is really the cubic root or not with exact integer arithmetic. (I assumed n
is an int
)
cand = int(n ** (1.0/3.0) + 0.1)
if cand**3 == n:
print(cand, "is the cube root of ", n)
processing = False
else:
n -= 1
Avoid any hacks with floats and tolerance levels, these are often unreliable for large inputs. A better tool for the job is to use a multiple precision arithmetic library such as gmpy2:
>>> import gmpy2
>>> root, exact = gmpy2.iroot(125, 3)
>>> print(root)
5
>>> print(exact)
True
The standard way to check for equality with floating point is to check for quality within a certain tolerance:
def floateq(a, b, tolerance=0.00000001):
return abs(a-b) < tolerance
Now you can check if the rounded, converted-to-an-integer version of the cube root is equal to the cube root itself within a certain tolerance:
def has_integer_cube_root(n):
floatroot = (n ** (1.0 / 3.0))
introot = int(round(floatroot))
return floateq(floatroot, introot)
Usage:
>>> has_integer_cube_root(125)
True
>>> has_integer_cube_root(126)
False
However, this is quite imprecise for your use case:
>>> has_integer_cube_root(40000**3)
True
>>> has_integer_cube_root(40000**3 + 1)
True
You can mess with the tolerance, but at some point, floating point numbers just won't be enough to have the accuracy you need.
EDIT: Yes, as the comment said, in this case you can check the result with integer arithmetic:
def has_integer_cube_root(n):
floatroot = (n ** (1.0 / 3.0))
introot = int(round(floatroot))
return introot*introot*introot == n
>>> has_integer_cube_root(40000**3)
True
>>> has_integer_cube_root(40000**3 + 1)
False
It's quite simple. Cast the floating point to a string and the string to a float. Like this
float(str(pow(125, (1./3))))
It's more straightforward to avoid the problem! For example:
mx = 12000 # maximum for cubes
crmx = int(mx**(1/3)) + 1 # largest test integer
result = max([n for n in range(crmx) if n**3 < mx])
# result = 22
Floating point arithmetic is always approximate. For example:
.99999999999999999.is_integer() gives True
.9999999999999999.is_integer() gives False
(Your interpreter's mileage may differ.)
The result of 125 ** (1.0/3.0) is never going to be an integer because that is a floating-point operation. This is much easier to do by looking at the cube instead. For instance, if you just want the largest number with an integer cube root below some number max, then you could:
max = 12000
cube_root = int(max ** (1.0/3.0)) # Take cube root and round to nearest integer
cubed = cube_root ** 3 # Find cube of this number
print str(cube_root) + " is the cube root of " + str(cubed) # Output result
The only sticking point for the above is if it happens to start the code with a max that is a cube root, and the cube root rounds to 4.9999999999999. When converted to an integer, this will round to 4, skipping the correct cube root (5) and going straight to "4 is the cube root of 64". You can get around this a few different ways. One way would be to convert the second line to:
cube_root = int(max ** (1.0/3.0) + 0.5)
This converts the "round down" operation of int() into a "round to nearest" operation.