Besides integers, consisting of whole numbers without fractions, Python also offers us the float type. Perhaps you’ve already seen floats without realizing it. After all, when you divide two integers in Python, the result of that division is often of type float.
In this article, I’ll explain what floats are and how to use them. We’ll also look at an important limitation of floats that you need to be aware of.
What is a Python float?
A Python float is a number with a fractional part, like 1.23, -0.5 or 2.0. The name comes from “floating point”: the computer stores the number as a set of digits plus an exponent that says where the decimal point goes. That’s why the same type can hold both very large numbers, like 1.5e20, and very small ones, like 3e-8.
Under the hood, a Python float is a 64-bit “double precision” number. It holds about 15 to 17 significant digits. That’s plenty for most uses, but as you’ll see further down, it does have consequences.
Creating floating point numbers
There are multiple ways to create floating-point numbers in Python. Most of the time, you will simply enter the number as-is:
# You can simply enter the number
f = 1.45Code language: Python (python)
But you may want to convert a number or a string to a float using the float() function. The variable f will become 2.0 in all cases below:
# f will become 2.0 in all cases below
f = float(2.0)
f = float(2)
f = float("2")Code language: Python (python)
You can also use scientific notation:
f = 1.45e3
# f is now 1450.0Code language: Python (python)
Working with Python floats
Working with floats is not much different from working with integers. You can do the usual addition, subtraction, multiplication, and division.
x = 1.45
y = 4.51
# Add x and y
z = x + y
# Subtract x and y
z = x - y
# Multiply x and y
z = x * y
# Divide x and y
z = x / yCode language: Python (python)
Round, floor and ceil
With floats, we often need to round numbers. For this, Python has the round function. The round function accepts two arguments: the number itself and an optional precision (the number of decimals). The default for this last number is 0, meaning that it will round to whole integers. Some examples:
>>> f = 1.4567
>>> round(f)
1
>>> round(f, 2)
1.46
>>> f = 1.54
>>> round(f)
2
>>> round(f, 1)
1.5Code language: Python (python)
There’s one surprise here. When a number is exactly halfway, round doesn’t always round up. Instead, it rounds to the nearest even number. This is called banker’s rounding, and it prevents a bias upward when you round lots of numbers:
>>> round(2.5)
2
>>> round(3.5)
4
>>> round(0.125, 2)
0.12Code language: Python (python)
On top of that, many decimal numbers can’t be stored exactly (more on that below). round(2.675, 2) gives 2.67, because the float 2.675 is actually stored as a tiny bit less than 2.675. If you only want to show a number with two decimals, you usually don’t need round at all. Use an f-string instead, as explained in formatting floats.
While round is available without importing, we need to do an import from the math library to floor and ceil a number.
The floor function rounds a float down to the nearest integer while the ceil function rounds a float up to the nearest integer. Here’s an example of how you can import and use these functions:
from math import floor, ceil
# Round 1.23 down to the nearest integer
x = floor(1.23) # x will be 1
# Round 1.23 up to the nearest integer
y = ceil(1.23) # y will be 2Code language: Python (python)
Comparisons
Just like other types of numbers, floats can be compared using comparison operators such as ==, !=, >, <, >=, and <=. For example, the following code uses the == operator to check if the float 1.23 is equal to the float 4.56:
x = 1.23
y = 4.56
if x == y:
print("x and y are equal")
else:
print("x and y are not equal")Code language: Python (python)
Be careful with == on floats, though. Because of the limited precision that I’ll explain in the next section, two calculations that should give the same result often differ in the last digit:
>>> 0.1 + 0.2 == 0.3
False
>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
TrueCode language: Python (python)
So when you compare the results of calculations, use math.isclose(). By default, it considers two numbers equal when they differ by less than a billionth of their size. You can change that with rel_tol. One catch: when you compare against zero, a relative tolerance doesn’t help, so pass an absolute tolerance with abs_tol:
>>> math.isclose(1000.0, 1000.1, rel_tol=1e-3)
True
>>> math.isclose(0.0, 1e-12)
False
>>> math.isclose(0.0, 1e-12, abs_tol=1e-9)
TrueCode language: Python (python)
The Python float has limited precision
Let’s do a little experiment. For extra dramatic effect, try this for yourself first:
>>> 0.3 + 0.1
0.4
>>> 0.1 + 3.8
3.9
>>> 0.1 + 0.2
# What is the output of this last statement?Code language: Python (python)
Did you expect the output to be 0.3 like any normal person would? I don’t blame you, but you’re wrong! The output is actually 0.30000000000000004. As absurd as this might seem, there’s an explanation.
First of all, some fractional numbers are endless. E.g., when you divide 1 by 3, you can round the result to 0.33, 0.333333333, or 0.333333333333333333. No matter what precision you choose, there will always be more 3’s. Since there’s a limited number of bits to store a float number, many numbers are a best-effort approximation. So floating-point numbers have limited precision because they are stored in a finite amount of memory, sometimes leading to unexpected results when performing arithmetic operations on floating-point numbers.
Solving the limited precision
The limited precision of floats is a fundamental property of how floats are represented in computer memory. Therefore, it is not possible to completely eliminate the issue of limited precision when working with floats.
However, there are a few ways that you can mitigate the effects of limited precision when working with floats in Python:
- Use the
decimalmodule: Thedecimalmodule in the Python standard library provides support for decimal floating-point arithmetic. Numbers like 0.1 are stored exactly, soDecimal("0.1") + Decimal("0.2")givesDecimal('0.3'). This makes it the usual choice for money. Create decimals from strings, not floats, or you copy the float’s error. The downside is that floats are much faster than using the decimal package. - Use the
fractionsmodule:fractions.Fractionstores a number as an exact fraction of two integers.Fraction(1, 10) + Fraction(2, 10)gives exactly3/10, andFraction(1, 3) * 3gives exactly1. Likedecimal, it’s slower than floats. - Compare with
math.isclose()instead of==, as shown in the comparisons section, and format your output with a fixed number of decimals. The tiny errors are still there, but they don’t affect your logic and your users won’t see them.
Converting floats to other data types
You can convert a float to other data types, such as int or str, using the built-in functions int() and str(). For example, the following code converts a float to an integer and another float to a string:
# Convert float to integer
my_int = int(1.22) # my_int will be 1
# Convert float to string
my_str = str(2.23) # my_str will be "2.23"Code language: Python (python)
Note that int() doesn’t round. It simply cuts off the fractional part, so int(1.99) is 1 and int(-1.99) is -1. Use round(), floor() or ceil() if you need something else.
Formatting floats
Most of the time, when you want “a float with two decimals,” you want to show it that way, not change the number itself. That’s what f-strings are for. Put a colon after the value and then a format specification:
>>> price = 1234.5678
>>> f"{price:.2f}"
'1234.57'
>>> f"{price:,.2f}"
'1,234.57'
>>> f"{price:.3e}"
'1.235e+03'
>>> f"{0.25:.0%}"
'25%'
>>> f"{0.1 + 0.2:.2f}"
'0.30'Code language: Python (python)
Here, .2f means two digits after the decimal point, the comma adds a thousands separator, e gives scientific notation, and % multiplies by 100 and adds a percent sign. The last example shows how formatting also hides the tiny precision error from the previous section.
Infinity and NaN
Floats have a few special values. float("inf") is positive infinity, which is larger than any other number, and float("-inf") is its negative counterpart. You also get infinity when a calculation becomes too large for a float, like 1e308 * 10. Then there’s float("nan"), short for “not a number,” which some calculations and data files use for a missing or undefined value:
>>> import math
>>> inf = float("inf")
>>> inf > 10**100
True
>>> nan = float("nan")
>>> nan == nan
False
>>> math.isnan(nan)
TrueCode language: Python (python)
NaN is not equal to anything, not even to itself. So never test for it with ==. Use math.isnan() instead, and math.isinf() to test for infinity.