float.is_integer()

The version of is_integer that can actually say False. It asks about the VALUE, not the type — 4.0 is a whole number that is still a float.

Float methodPython 2.6+Live demo
Common call
x.is_integer()
Returns
bool — True when nothing is lost by converting to int
Replaces
x == int(x), which raises on inf and nan
Watch out
True does not mean the object is an int — 4.0 is still a float
float.is_integer()
→ bool

Demo

Live evaluation
Try:
Inputs
nfloata number, e.g. 4.0 or 4.5
Output
float(4).is_integer()
True

is_integer asks whether the float sits exactly on a whole number. 4.0 does, so it is True; 4.5 does not, so it is False. Negative whole numbers count, and so does zero. The demo wraps the input in float() so the call preview is unambiguous — 4 and 4.0 are the same value in the demo box, but only one of them is a float in Python.

Common patterns

Convert only when it is safe
Guards the int() call so nothing is silently truncated.
if x.is_integer():
    n = int(x)
else:
    raise ValueError("expected a whole number")
Validate parsed JSON numbers
JSON gives floats for anything with a decimal point; this checks the value, not the type.
if not qty.is_integer():
    raise ValueError("quantity must be whole")
Format whole floats without the .0
Prints 4 rather than 4.0 when there is nothing after the point.
text = str(int(x)) if x.is_integer() else str(x)

Examples

1. Whole float
(4.0).is_integer()
Returns
True
2. Has a fraction
(4.5).is_integer()
Returns
False
3. Zero
(0.0).is_integer()
Returns
True
4. Negative whole
(-2.0).is_integer()
Returns
True
5. Infinity is not
float('inf').is_integer()
Returns
False
6. Still a float
type(4.0)
Returns
<class 'float'>

Pitfalls

1. True does not mean you have an int
The name invites a type reading. 4.0 is a whole NUMBER but still a float object, so isinstance checks and repr output are unaffected by a True answer.
Not a type test
x = 4.0
x.is_integer(), isinstance(x, int)
(True, False)
Convert if you need one
n = int(x) if x.is_integer() else None
4, a real int
2. Float error can make the answer surprising
Arithmetic that should land on a whole number sometimes lands next to it. is_integer reports the value you actually have, not the one you intended.
Not quite whole
(0.1 * 3 * 10).is_integer()
False # it is 3.0000000000000004
Round first
round(0.1 * 3 * 10, 9).is_integer()
True
3. inf and nan are False, not errors
Neither is a whole number, so both answer False rather than raising. That is friendlier than x == int(x), which raises on both.
The old idiom raises
x = float('inf')
x == int(x)
OverflowError: cannot convert float infinity to integer
is_integer copes
float('inf').is_integer()
False
4. Large floats are all whole
Above 2 ** 53 consecutive floats are more than 1 apart, so every value is whole. is_integer returns True even though the number lost precision long ago.
Misleading True
(1e300).is_integer()
True
Check magnitude too
x.is_integer() and abs(x) < 2 ** 53
whole AND exact

When to use

Use it
  • Deciding whether int(x) would lose anything
  • Validating that a parsed number is a whole count
  • Formatting output without a trailing .0
  • Replacing x == int(x), which raises on inf and nan
Reach for something else
  • You want a TYPE check → isinstance(x, int)
  • The value came from float arithmetic → round first
  • Money or exact decimals → decimal.Decimal

Notes

Complexity
O(1) — compares the value against its own floor
Return
A bool; False for inf and nan rather than an exception
CPython impl
Objects/floatobject.c :: float_is_integer
Memory
No allocation — True and False are singletons
Thread-safe
Yes — floats are immutable

FAQ

It works for ordinary numbers but raises OverflowError on inf and ValueError on nan, because int() cannot convert either. is_integer answers False for both, so it needs no guard.

float('inf').is_integer()   # False
int(float('inf'))           # OverflowError

History

2.6
float.is_integer added.
3.12
int gained a matching is_integer, so both numeric types now answer.