zaro

How to Check if a Number is a Whole Number in Python?

Published in Python Number Checking 6 mins read

In Python, you can determine if a number is a whole number (an integer value, regardless of its data type) using several effective methods, each suited for different scenarios and precision requirements.

Understanding Whole Numbers in Python

When we talk about "whole numbers" in Python, we typically mean numbers that have no fractional component. This includes int types (like 5, -3, 0) and float types that represent an integer value (like 5.0, -3.0).

Common Methods to Check for Whole Numbers

Here are the most common and reliable ways to check if a number is a whole number in Python:

1. Using the Modulo Operator (%)

The modulo operator returns the remainder of a division. If a number is a whole number, dividing it by 1 will yield a remainder of 0. This method is versatile as it works for both int and float types.

def is_whole_number_modulo(num):
    return num % 1 == 0

print(f"5 is a whole number: {is_whole_number_modulo(5)}")        # Output: True
print(f"5.0 is a whole number: {is_whole_number_modulo(5.0)}")    # Output: True
print(f"5.5 is a whole number: {is_whole_number_modulo(5.5)}")    # Output: False
print(f"-3 is a whole number: {is_whole_number_modulo(-3)}")      # Output: True
print(f"-3.0 is a whole number: {is_whole_number_modulo(-3.0)}")  # Output: True
print(f"-3.1 is a whole number: {is_whole_number_modulo(-3.1)}")  # Output: False

2. Using the is_integer() Method (for Floats)

Python's built-in float type has a convenient method called is_integer(). This method specifically checks if a float instance has an integer value. It's concise and directly expresses the intent.

def is_whole_number_float_method(num):
    return isinstance(num, float) and num.is_integer()

print(f"5.0 is a whole number: {is_whole_number_float_method(5.0)}")    # Output: True
print(f"5.5 is a whole number: {is_whole_number_float_method(5.5)}")    # Output: False
print(f"5 (int) is a whole number: {is_whole_number_float_method(5)}")  # Output: False (because it's not a float)

Note: This method explicitly checks if the number is a float and an integer value. If you want to include int types, you'd combine it with a type check for int.

3. Using Type Checking

The most straightforward way to check if a number is an integer data type is by using isinstance(). However, this only tells you about the type, not if a float value represents a whole number.

def is_integer_type(num):
    return isinstance(num, int)

print(f"5 is an integer type: {is_integer_type(5)}")        # Output: True
print(f"5.0 is an integer type: {is_integer_type(5.0)}")    # Output: False

For a comprehensive check that covers both int types and float types that represent whole numbers, combine isinstance(num, int) with one of the other methods for floats.

4. Using math.isclose() for Robust Floating-Point Comparisons

Due to the nature of floating-point representation, direct equality checks (== or % 1 == 0) can sometimes lead to unexpected results with very small floating-point inaccuracies. The math.isclose() function, introduced in Python 3.5, provides a robust way to compare two floating-point numbers for approximate equality, accounting for these potential errors.

You can use math.isclose() to check if a number is approximately equal to its rounded integer version. This is particularly useful when numbers might be the result of complex calculations that introduce tiny precision errors.

import math

def is_whole_number_isclose(num, rel_tol=1e-9, abs_tol=0.0):
    # Check if the number is an int (exact match)
    if isinstance(num, int):
        return True
    # If it's a float, check if it's close to its integer representation
    elif isinstance(num, float):
        return math.isclose(num, round(num), rel_tol=rel_tol, abs_tol=abs_tol)
    return False

print(f"5.0 is a whole number: {is_whole_number_isclose(5.0)}")          # Output: True
print(f"5.5 is a whole number: {is_whole_number_isclose(5.5)}")          # Output: False
print(f"5.000000000000001 is a whole number: {is_whole_number_isclose(5.000000000000001)}") # Output: True (due to tolerance)
print(f"4.999999999999999 is a whole number: {is_whole_number_isclose(4.999999999999999)}") # Output: True (due to tolerance)
print(f"5 is a whole number: {is_whole_number_isclose(5)}")              # Output: True

In this method, round(num) gives the nearest integer value. math.isclose() then checks if num is "close enough" to round(num). The rel_tol (relative tolerance) and abs_tol (absolute tolerance) parameters allow you to fine-tune the precision.

5. Using int() Conversion

You can convert the number to an integer and then compare it to the original number. If they are identical, the original number represents a whole number. This works for both int and float types.

def is_whole_number_int_conversion(num):
    # This comparison implicitly handles both int and float types.
    # int() truncates the decimal part. If the original number is equal
    # to its truncated integer version, it's a whole number.
    return num == int(num)

print(f"5 is a whole number: {is_whole_number_int_conversion(5)}")        # Output: True
print(f"5.0 is a whole number: {is_whole_number_int_conversion(5.0)}")    # Output: True
print(f"5.5 is a whole number: {is_whole_number_int_conversion(5.5)}")    # Output: False
print(f"-3.0 is a whole number: {is_whole_number_int_conversion(-3.0)}")  # Output: True
print(f"-3.1 is a whole number: {is_whole_number_int_conversion(-3.1)}")  # Output: False

Choosing the Right Method

The best method depends on your specific needs and the expected input data types.

Method Best For Pros Cons
num % 1 == 0 General use (int and float) Simple, intuitive for remainder concept. Can be susceptible to minor float precision issues.
my_float.is_integer() Specific float objects Explicit, clear intent, optimized for floats. Only works directly on float types; doesn't check int types.
isinstance(num, int) Checking if the type is an int Fastest for type checking. Doesn't verify if a float value is a whole number.
math.isclose(num, round(num)) Robust float comparison Handles floating-point inaccuracies, very robust. Requires import math, slightly more verbose.
num == int(num) General use (int and float) Simple, easy to understand. Can also be susceptible to minor float precision issues.

For most common cases where you expect numbers that are either true integers or floats that perfectly represent integers (e.g., 5 or 5.0), num % 1 == 0 or num == int(num) are excellent choices. If your application involves extensive floating-point arithmetic and precision is paramount, math.isclose() provides the most robust solution.