4

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 like 125, that should return a cube root of 5 but instead Python returns 4.99999 Example:

>>> 125 ** (1.0/3.0)
4.999999999999999

This is my code:

processing = True
n = 12000
while processing:


    if (n ** (1.0/3.0)).is_integer() == True:
        print((n ** (1.0/3.0)), "is the cube root of ", n)
        processing = False
    else:
        n -= 1
chopper draw lion4
  • 12,401
  • 13
  • 53
  • 100

6 Answers6

6

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    
Claudiu
  • 224,032
  • 165
  • 485
  • 680
  • 1
    better to check using `return n == introot ** 3`, then you get a correct answer for most sane values. – cobbal Feb 05 '14 at 19:19
  • 2
    Fails for large numbers. `has_integer_cube_root((123456**3)-1) True` – tmj Feb 05 '14 at 19:21
1

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
nodakai
  • 7,773
  • 3
  • 30
  • 60
1

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.

Kimby
  • 124
  • 1
  • 4
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
wim
  • 338,267
  • 99
  • 616
  • 750
-1

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))))

Karanja Denis
  • 358
  • 3
  • 8
-1

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.)

rwilsker
  • 107
  • 1
  • 4