294

I am trying to find the largest cube root that is a whole number, that is less than 12,000.

processing = True
n = 12000
while processing:
    n -= 1
    if n ** (1/3) == #checks to see if this has decimals or not

I am not sure how to check if it is a whole number or not though! I could convert it to a string then use indexing to check the end values and see whether they are zero or not, that seems rather cumbersome though. Is there a simpler way?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
chopper draw lion4
  • 12,401
  • 13
  • 53
  • 100
  • 4
    it would make it easier to work from the cube root n --> (n * n * n < 12000) – suspectus Feb 05 '14 at 17:09
  • Btw do you mean largest integer cube? That can be computed for small values by rounding down with `int(12000**(1/3))**3` – qwr Dec 30 '22 at 02:50

15 Answers15

553

To check if a float value is a whole number, use the float.is_integer() method:

>>> (1.0).is_integer()
True
>>> (1.555).is_integer()
False

The method was added to the float type in Python 2.6.

Take into account that in Python 2, 1/3 is 0 (floor division for integer operands!), and that floating point arithmetic can be imprecise (a float is an approximation using binary fractions, not a precise real number). But adjusting your loop a little this gives:

>>> for n in range(12000, -1, -1):
...     if (n ** (1.0/3)).is_integer():
...         print n
... 
27
8
1
0

which means that anything over 3 cubed, (including 10648) was missed out due to the aforementioned imprecision:

>>> (4**3) ** (1.0/3)
3.9999999999999996
>>> 10648 ** (1.0/3)
21.999999999999996

You'd have to check for numbers close to the whole number instead, or not use float() to find your number. Like rounding down the cube root of 12000:

>>> int(12000 ** (1.0/3))
22
>>> 22 ** 3
10648

If you are using Python 3.5 or newer, you can use the math.isclose() function to see if a floating point value is within a configurable margin:

>>> from math import isclose
>>> isclose((4**3) ** (1.0/3), 4)
True
>>> isclose(10648 ** (1.0/3), 22)
True

For older versions, the naive implementation of that function (skipping error checking and ignoring infinity and NaN) as mentioned in PEP485:

def isclose(a, b, rel_tol=1e-9, abs_tol=0.0):
    return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Not knowing python, this sort of statement would make me nervous as it seems to require perfect math to work in the real world. – Peter M Feb 05 '14 at 17:16
  • 1
    @PeterM: The method indeed only returns `True` if there are no decimals at all. There may be a misunderstanding on the part of the OP about floating point arithmetic and precision, of course. – Martijn Pieters Feb 05 '14 at 17:17
  • 1
    @MartijnPieters Yeah and one small slip in a floating point calculation and all of a sudden you have these little, unwanted decimals like 0.00000000000000000001 – Peter M Feb 05 '14 at 17:19
  • 1
    @PeterM: and in Python 2 the default representation will round to 16 digits; `1.0000000000000001` is displayed as `1.0`, in 3 the shortest string representation that produces the same value is shown. – Martijn Pieters Feb 05 '14 at 17:21
  • Your `range(12000, -1, -1)` could be (imo, more cleanly) rewritten as `reversed(range(12000+1))` – cs95 Apr 26 '19 at 04:51
  • @cs95: I don’t agree, and not really all that relevant to the question. – Martijn Pieters Apr 26 '19 at 07:40
  • Sure it isn't relevant to the question, but it is to your answer since you're iterating in reverse. Using reversed is a bit more readable... Just my 2c. – cs95 Apr 26 '19 at 10:35
  • This is great when you have a variable that you are sure is a float but I am surprised that `(1).is_integer()` raises `AttributeError: 'int' object has no attribute 'is_integer'`. – Bill Dec 06 '21 at 02:56
  • @Bill: `is_integer()` only applies to `float` because it tests the _value_, where `(3.14).is_integer()` is `False`, and `(3.0).is_integer()` while `True`. `int` is not a subclass of `float` nor is `is_integer()` part of the [`Real` abstract base class](https://docs.python.org/3/library/numbers.html#numbers.Real). – Martijn Pieters Jan 19 '22 at 18:45
  • Indeed but it might still be nice to have two equivalent methods that test to see if a value (int or float) is a whole number. Rather than having to use `isinstance(x, int) or x.is_integer()` or `float(x).is_integer()`. But that's okay, I can live with these solutions! – Bill Jan 19 '22 at 19:12
69

We can use the modulo (%) operator. This tells us how many remainders we have when we divide x by y - expresses as x % y. Every whole number must divide by 1, so if there is a remainder, it must not be a whole number.

This function will return a boolean, True or False, depending on whether n is a whole number.

def is_whole(n):
    return n % 1 == 0
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
MagikCow
  • 863
  • 8
  • 13
  • This is the better answer because it also works if `n` happens to be an integer. The accepted answer will fail in that case. – emilaz Sep 07 '21 at 13:12
20

You could use this:

if k == int(k):
    print(str(k) + " is a whole number!")
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Juri Robl
  • 5,614
  • 2
  • 29
  • 46
  • 7
    it fails for [larger numbers](http://stackoverflow.com/a/26447472/4279) while `.is_integer()` continues to work. – jfs Oct 19 '14 at 11:49
  • Your link IMHO doesn't show that it doesn't work. It just shows that large floats lose precision. `is_integer` uses a similar method (`o = (floor(x) == x) ? Py_True : Py_False;`). But I agree, one should use `is_integer()` as it is much clearer. – Juri Robl Oct 19 '14 at 11:57
  • 2
    yes. It just shows that large float may lose precision i.e., `large_float == large_int` may fail even if `large_float == float(large_int)`. – jfs Oct 19 '14 at 12:11
  • Do you have an example where it fails? I tested it with large numbers (e.g. the one in your link) and for them it works. – Juri Robl Oct 19 '14 at 12:14
  • 2
    `123456789012345678901234567890.0 != 123456789012345678901234567890` but `123456789012345678901234567890.0 == float(123456789012345678901234567890)` – jfs Oct 19 '14 at 12:33
  • 3
    Yeah, but `k = 123456789012345678901234567890.0` then `k == int(k)` is True, which is the correct answer. – Juri Robl Oct 19 '14 at 12:35
  • yes, your answer is technically correct. It is just misleading. – jfs Oct 19 '14 at 12:40
13

You don't need to loop or to check anything. Just take a cube root of 12,000 and round it down:

r = int(12000**(1/3.0))
print r*r*r # 10648
georg
  • 211,518
  • 52
  • 313
  • 390
9

You can use a modulo operation for that.

if (n ** (1.0/3)) % 1 != 0:
    print("We have a decimal number here!")
Jakub Jirutka
  • 10,269
  • 4
  • 42
  • 35
7

How about

if x%1==0:
    print "is integer"
Daniel
  • 981
  • 3
  • 11
  • 15
6

Wouldn't it be easier to test the cube roots? Start with 20 (20**3 = 8000) and go up to 30 (30**3 = 27000). Then you have to test fewer than 10 integers.

for i in range(20, 30):
    print("Trying {0}".format(i))
    if i ** 3 > 12000:
        print("Maximum integral cube root less than 12000: {0}".format(i - 1))
        break
hughdbrown
  • 47,733
  • 20
  • 85
  • 108
  • 1
    Moreover, floats have round-off errors so that you can miss number when calculating if `n**(1/3)` is integer. For example on my computer ` 10648**(1/3)=21.999999999999996 ` instead of `22`: problem! With this answer's method there is no such problem. I think this is the only correct solution from a mathematic point of view (others solutions are Python-correct). – JPG Feb 05 '14 at 17:16
5

The above answers work for many cases but they miss some. Consider the following:

fl = sum([0.1]*10)  # this is 0.9999999999999999, but we want to say it IS an int

Using this as a benchmark, some of the other suggestions don't get the behavior we might want:

fl.is_integer() # False

fl % 1 == 0     # False

Instead try:

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)

def is_integer(fl):
    return isclose(fl, round(fl))

now we get:

is_integer(fl)   # True

isclose comes with Python 3.5+, and for other Python's you can use this mostly equivalent definition (as mentioned in the corresponding PEP)

control_fd
  • 348
  • 2
  • 8
3

Just a side info, is_integer is doing internally:

import math
isInteger = (math.floor(x) == x)

Not exactly in python, but the cpython implementation is implemented as mentioned above.

user1767754
  • 23,311
  • 18
  • 141
  • 164
1

All the answers are good but a sure fire method would be

def whole (n):
     return (n*10)%10==0

The function returns True if it's a whole number else False....I know I'm a bit late but here's one of the interesting methods which I made...

Edit: as stated by the comment below, a cheaper equivalent test would be:

def whole(n):
    return n%1==0
random_npc
  • 171
  • 1
  • 12
  • 3
    This should be no functionally different than `n % 1 == 0`. In this case, you're doing two operations which is more expensive for a cheaper equivalent test. – Zchpyvr Nov 05 '19 at 14:36
1

You can use something like:

num = 1.9899
bool(int(num)-num)
#returns True

if it is True, It means it holds some value, hence not a whole number. Else

num = 1.0
bool(int(num)-num)
# returns False
DARK_C0D3R
  • 2,075
  • 16
  • 21
0
>>> def is_near_integer(n, precision=8, get_integer=False):
...     if get_integer:
...         return int(round(n, precision))
...     else:
...         return round(n) == round(n, precision)
...
>>> print(is_near_integer(10648 ** (1.0/3)))
True
>>> print(is_near_integer(10648 ** (1.0/3), get_integer=True))
22
>>> for i in [4.9, 5.1, 4.99, 5.01, 4.999, 5.001, 4.9999, 5.0001, 4.99999, 5.000
01, 4.999999, 5.000001]:
...     print(i, is_near_integer(i, 4))
...
4.9 False
5.1 False
4.99 False
5.01 False
4.999 False
5.001 False
4.9999 False
5.0001 False
4.99999 True
5.00001 True
4.999999 True
5.000001 True
>>>
  • 1
    Here are some guidelines for [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer). This provided answer may be correct, but it could benefit from an explanation. Code only answers are not considered "good" answers. From [review](https://stackoverflow.com/review). – Trenton McKinney Sep 24 '19 at 00:50
0

This problem has been solved, but I would like to propose an additional mathematical-based solution for funcies.

The benefit of this approach is that it calculates the whole number part of your number, which may be beneficial depending on your general task.

Algorithm:

  • Decompose whole number part of your number its a sum of its decimals (e.g., 327=3*100+2*10+7*1)
  • take difference between calculated whole number and number itself
  • decide whether difference is close enough to be considered an integer.
from math import ceil, log, isclose

def is_whole(x: float) -> bool:
    n_digits = ceil(log(x,10)) # number of digits of decimals at or above ones
    digits = [(n//(10**i))%10 for i in range(n_digits)] # parse digits of `x` at or above ones decimal
    whole = 0 # will equal the whole number part of `x`
    for i in range(n_digits):
        decimal = 10**i
        digit = digits[i]
        whole += digit*decimal
    
    diff = whole - x
    return isclose(diff, 0.0)

NOTE: the idea of parsing digits of a number was realized from here

Erick Platero
  • 81
  • 1
  • 2
-1

You can use the round function to compute the value.

Yes in python as many have pointed when we compute the value of a cube root, it will give you an output with a little bit of error. To check if the value is a whole number you can use the following function:

def cube_integer(n):
    if round(n**(1.0/3.0))**3 == n:
        return True
    return False

But remember that int(n) is equivalent to math.floor and because of this if you find the int(41063625**(1.0/3.0)) you will get 344 instead of 345.

So please be careful when using int withe cube roots.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Anivarth
  • 619
  • 5
  • 19
-1

Try using:

int(val) == val

It will give lot more precision than any other methods.

Tim Diekmann
  • 7,755
  • 11
  • 41
  • 69
Nishant Ingle
  • 793
  • 1
  • 8
  • 12