Check if a Number Is Between Two Values in Python

To check whether a Python variable sits between two numbers, chain the comparison the way you’d write it on paper:

if 18 <= age <= 65:
    print("working age")

That’s the whole answer. Python allows low <= x <= high directly, which most languages don’t, so you rarely need and.

Below I check the corners that actually bite: range(), floats, and arrays. Everything ran on Python 3.12.5.

Inclusive or exclusive between two numbers

Swap the operators to decide whether the edges count:

age = 34

# Python lets you chain the comparison, exactly like the maths
if 18 <= age <= 65:
    print("working age")

temperature = 21.5
print("comfortable:", 18 <= temperature <= 24)

# the boundaries are yours to choose
score = 100
print("0 to 100 inclusive :", 0 <= score <= 100)
print("0 to 100 exclusive :", 0 < score < 100)

Output:

working age
comfortable: True
0 to 100 inclusive : True
0 to 100 exclusive : False
Command Prompt showing Python chained comparisons checking whether values fall between two numbers with inclusive and exclusive bounds
<= includes the boundary, < excludes it.

Mixing them is fine too. 0 <= score < 100 reads as “zero or more, but under a hundred”, which is how most ranges in code actually behave.

Why chaining beats and for a between check

1 <= x <= 10 and 1 <= x and x <= 10 give the same answer, but they are not the same code.

The chained form evaluates the middle expression once. The spelled-out form evaluates it twice:

calls = []

def reading():
    calls.append(1)          # count how many times this runs
    return 5

calls.clear()
chained = 1 <= reading() <= 10
print("chained  :", chained, "| reading() ran", len(calls), "time(s)")

calls.clear()
spelled_out = 1 <= reading() and reading() <= 10
print("with and :", spelled_out, "| reading() ran", len(calls), "times")

Output:

chained  : True | reading() ran 1 time(s)
with and : True | reading() ran 2 times

With a plain variable that costs nothing. With a function call, a database lookup or anything with a side effect, running it twice is a real bug waiting to happen.

That alone is a good reason to make chaining your habit.

Should you use range() for a between check?

x in range(low, high) looks readable and gets recommended a lot. It has three problems:

# range() looks tempting, and it has three problems

print("10 in range(1, 10) :", 10 in range(1, 10), "  <- stop is excluded")
print("1 <= 10 <= 10      :", 1 <= 10 <= 10, "   <- what you probably meant")

print("2.5 in range(1, 5) :", 2.5 in range(1, 5), "  <- no error, just wrong")
print("1 <= 2.5 <= 5      :", 1 <= 2.5 <= 5)

import timeit
membership = timeit.timeit("500 in range(0, 1_000_000)", number=100_000) / 100_000 * 1e6
comparison = timeit.timeit("0 <= 500 < 1_000_000", number=100_000) / 100_000 * 1e6
print(f"in range(): {membership:.3f} us | chained compare: {comparison:.3f} us")

Output:

10 in range(1, 10) : False   <- stop is excluded
1 <= 10 <= 10      : True    <- what you probably meant
2.5 in range(1, 5) : False   <- no error, just wrong
1 <= 2.5 <= 5      : True
in range(): 0.087 us | chained compare: 0.032 us
Command Prompt showing that 10 in range 1 to 10 is False, 2.5 in range 1 to 5 is False, and timings comparing range membership with a chained comparison
Excludes the stop, silently rejects floats, and is slower.
  • The stop value is excluded, so 10 in range(1, 10) is False.
  • A float never matches. 2.5 in range(1, 5) returns False with no error at all.
  • It’s slower. Membership is a constant-time check in Python 3, not a scan, but a comparison is still quicker.

The float case is the dangerous one, because nothing tells you anything went wrong. Use range() for looping and comparisons for testing.

Float precision in a between check

This isn’t specific to range checks, but it shows up here constantly:

total = 0.1 + 0.2
print("0.1 + 0.2 =", total)

print("0.3 <= total <= 0.3 :", 0.3 <= total <= 0.3)      # False, and correctly so

import math
print("math.isclose        :", math.isclose(total, 0.3))

# for a range with float edges, give yourself a tolerance
tolerance = 1e-9
print("within tolerance    :", 0.3 - tolerance <= total <= 0.3 + tolerance)

Output:

0.1 + 0.2 = 0.30000000000000004
0.3 <= total <= 0.3 : False
math.isclose        : True
within tolerance    : True
Command Prompt showing that 0.1 plus 0.2 does not equal 0.3 exactly and fails a strict between test, with math.isclose returning True
0.1 + 0.2 lands just above 0.3, so a strict test fails.

When the boundaries are computed rather than typed, add a small tolerance or reach for math.isclose. The same care applies when you round to two decimal places for display.

A reusable check, and other types

Wrap it in a function when the same range is tested in several places:

def between(value, low, high, inclusive=True):
    """True when value falls inside the range."""
    return low <= value <= high if inclusive else low < value < high

print(between(5, 1, 10))
print(between(10, 1, 10))
print(between(10, 1, 10, inclusive=False))

# chaining is not only for numbers
print("dates  :", "2026-01-01" <= "2026-06-15" <= "2026-12-31")

import datetime as dt
today = dt.date(2026, 6, 15)
print("real dates:", dt.date(2026, 1, 1) <= today <= dt.date(2026, 12, 31))

# clamp instead of test, when you want the value pulled into range
print("clamped:", max(1, min(99, 150)))

Output:

True
True
False
dates  : True
real dates: True
clamped: 99

Chained comparison isn’t limited to numbers. Dates, strings and anything else with an ordering works the same way, which makes date-range filters pleasantly short.

The last line is worth stealing: max(low, min(high, value)) clamps a value into range instead of testing it.

Checking a whole array or column

On a NumPy array or a pandas column you want one answer per element, and the syntax changes:

import numpy as np
import pandas as pd

readings = np.array([12.5, 19.0, 24.8, 31.2, 8.4])

# element-wise: `and` will not work here, use & or the NumPy helper
inside = (18 <= readings) & (readings <= 25)
print("mask :", inside)
print("kept :", readings[inside])
print("count:", inside.sum(), "of", readings.size)

series = pd.Series(readings)
print("pandas .between():", series.between(18, 25).tolist())

Output:

mask : [False  True  True False False]
kept : [19.  24.8]
count: 2 of 5
pandas .between(): [False, True, True, False, False]
Command Prompt showing a NumPy boolean mask for values between 18 and 25 and the equivalent pandas between call
& for NumPy, or .between() if you’re in pandas.

Note the brackets around each comparison. & binds more tightly than <=, so leaving them out gives a confusing error.

And and simply doesn’t work on arrays, because it needs one true-or-false answer and an array has many:

import numpy as np

readings = np.array([12.5, 19.0, 24.8])

try:
    print(18 <= readings and readings <= 25)
except ValueError as error:
    print("ValueError:", error)

What NumPy says:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

That message is one of the most-searched NumPy errors there is. It means “use &“, and it shows up whenever array logic meets Python’s keywords.

More Python basics worth a read:

Frequently asked questions

How do I check if a number is between two values in Python?

Chain the comparison: low <= x <= high. Python evaluates it as one expression, which is documented under comparisons in the language reference.

Can I write a < x < b in Python?

Yes. Chained comparison is built into the language, and it evaluates the middle expression only once.

Should I use range() to check a range?

No. range() excludes the stop value, silently returns False for floats, and is slower than a comparison.

How do I include the boundaries?

Use <= on both sides. Use < where you want the edge excluded, and you can mix the two.

Why does my float range check fail?

Because 0.1 + 0.2 is not exactly 0.3. Add a small tolerance to the bounds, or compare with math.isclose.

How do I check a range on a NumPy array or pandas column?

Use (low <= arr) & (arr <= high) with brackets, or series.between(low, high) in pandas.

Why do I get “truth value of an array is ambiguous”?

You used and on an array. Replace it with &, which combines element by element.