Reputation: 110382
I'm looking to differentiate between a number like
2.0
or 2
and an actual fractional number such as 2.4
. What would be the best way to do this? Currently I'm doing:
def is_fractional(num):
if not str(num).replace('.','').isdigit(): return
return float(num) != int(num)
>>> is_fractional(2)
False
>>> is_fractional(2.1)
True
>>> is_fractional(2.0)
False
>>> is_fractional('a')
>>>
Upvotes: 5
Views: 2850
Reputation: 32898
Python includes a fractions
module that generates fractions (rational numbers) from strings, float
s, integers, and much more. Just create a Fraction
and check whether its denominator is other than 1 (the Fraction
constructor will automatically reduce the number to lowest terms):
from fractions import Fraction
def is_fractional(num):
return Fraction(num).denominator != 1
Note that the method above may raise an exception if the conversion to a Fraction fails. In this case, it's not known whether the object is fractional.
Upvotes: 1
Reputation: 88428
That operation is built-in:
>>> 5.0.is_integer()
True
>>> 5.00000001.is_integer()
False
>>> 4.9999999.is_integer()
False
Documentation is here.
ADDENDUM
The initial solution only works for float
. Here's a more complete answer, with tests:
from decimal import Decimal
def is_integer(x):
if isinstance(x, int):
return True
elif isinstance(x, float):
return x.is_integer()
elif isinstance(x, Decimal):
return x.as_integer_ratio()[1] == 1
return False
good = [
0,
0.0,
3,
-9999999999999999999999,
-2.0000000000000,
Decimal("3.000000"),
Decimal("-9")
]
bad = [
-9.99999999999999,
"dogs",
Decimal("-4.00000000000000000000000000000000001"),
Decimal("0.99999999999999999999999999999999999")
]
for x in good:
assert is_integer(x)
for x in bad:
assert not is_integer(x)
print("All tests passed")
Upvotes: 11
Reputation: 2105
Here is one way to do it (assuming e.g. 2/2 is not "fractional" in the sense you have in mind):
# could also extend to other numeric types numpy.float32
from decimal import Decimal
def is_frac(n):
numeric_types = (int, float, Decimal)
assert isinstance(n, numeric_types), 'n must be numeric :/'
# (ints are never fractions)
if type(n) is int: return False
return n != float(int(n))
# various sorts of numbers
ns = [-1, -1.0, 0, 0.1, 1, 1.0, 1., 2.3, 1e0, 1e3, 1.1e3,
Decimal(3), Decimal(3.0), Decimal(3.1)]
# confirm that values are as expected
dict(zip(ns, [is_frac(n) for n in ns]))
This will only work if n
is an int
or a float
or decimal.Decimal
. But you could extend it to handle other numeric types such as numpy.float64
or numpy.int32
by just including them in numeric_types
.
Upvotes: 0
Reputation: 489253
If some of your numbers are decimal.Decimal
s, they might have range issues where conversion to float fails, or drops the fractional part that actually exists, depending on their precision:
>>> import decimal
>>> x = decimal.Decimal('1.00000000000000000000000000000000000001')
>>> str(x)
'1.00000000000000000000000000000000000001'
>>> float(x).is_integer()
True
>>> y = decimal.Decimal('1e5000')
>>> str(y)
'1E+5000'
>>> float(y)
inf
The str
method will generally work (modulo problem cases like the one illustrated above), so you could stick with that, but it might be better to attempt to use is_integer
and use a fallback if that fails:
try:
return x.is_integer()
except AttributeError:
pass
(as others note, you'll need to check for int
and long
here as well, if those are allowed types, since they are integers by definition but lack an is_integer
attribute).
At this point, it's worth considering all of the other answers, but here's a specific decimal.Decimal handler:
# optional: special case decimal.Decimal here
try:
as_tuple = x.as_tuple()
trailing0s = len(list(itertools.takewhile(lambda i: i == 0, reversed(as_tuple[1]))))
return as_tuple[2] + trailing0s < 0
except (AttributeError, IndexError): # no as_tuple, or not 3 elements long, etc
pass
Upvotes: 3
Reputation: 46
If you are dealing with decimal
module or with a float
object, you can do this easily:
def is_factional(num):
return isinstance(num, (float, Decimal))
Upvotes: 0
Reputation: 530
Why do not check if the difference between the truncation to integer and the exact value is not zero?
is_frac = lambda x: int(x)-x != 0
Upvotes: 3