The float type in Python: decimal numbers

The float type carries decimal numbers in Python. A plain division is enough to make one, and its binary precision has limits worth knowing.
5 min read
Believemy logo

Definition

Whole numbers do the job as long as you are counting things. They stop doing it the moment you measure or share something out: 1.75 metres, 19.99 euros or an average of 12.5 land on no round figure at all. The float type exists for those values with a decimal part.

The decimal separator is the dot, never the comma, which in Python separates items instead. Writing an amount with a comma therefore raises no error at all: it builds two numbers instead of one, far harder to spot. A variable becomes a float as soon as a decimal point shows up in its value, with nothing to declare.

PYTHON
price = 19.99
rate = 0.2
total = price * (1 + rate)

print(type(total))
# <class 'float'>   the result inherited the decimal point

The name comes from "floating point": the position of the point is not fixed. The number is held as a run of digits plus an offset, and that offset moves so that 0.000012 and 12000000.0 both fit in the same amount of room. That is what separates it from int, which only knows whole numbers.

Nothing in the code says which of the two is being handled: asking for the type of a value settles the question as soon as a calculation returns something unexpected.


One division is enough to make one

A .0 always ends up appearing on screen, on a counter where nobody ever wrote a decimal point. One rule explains it: as soon as a decimal number enters a calculation, the result is a decimal number. Plain division produces one even between two whole numbers, because it gives the exact quotient and not the truncated one.

The table below gathers the most common cases; the column to follow is the one on the right.

CalculationResultType obtained
7 / 23.5float, always
6 / 23.0float, despite the round count
7 // 23int, this is integer division
3 + 0.53.5float, by contamination
2 ** 0.51.4142...float, the square root

The second row is the one that always surprises: the calculation comes out even, and the result is still a decimal number. A counter that met a division will therefore print "3.0" for the rest of the program. Fixing that on screen treats the symptom; the integer division operator //, or an explicit conversion, settle the matter where it starts.


The precision trap

A computer stores nothing in base ten. A decimal number is written in binary in a fixed amount of room, and many ordinary decimals have no finite binary form, starting with 0.1. Python then holds the closest value it can represent, and that gap surfaces as soon as numbers are added up.

PYTHON
0.1 + 0.2
# 0.30000000000000004   the gap does not fade, it piles up

0.1 + 0.2 == 0.3
# False

This is not a flaw in Python: every language following the same standard gives the same answer. The practical consequence is very real: comparing two decimal numbers with == inside a condition ends up surprising someone, even on values that look identical on screen. The way around it is to compare a gap rather than an equality, with math.isclose(a, b) or by checking that abs(a - b) stays under a chosen threshold.

Good to know

A float keeps roughly fifteen to seventeen significant digits: plenty for measurements or averages, not enough as soon as a value has to stay exact down to the cent.


Converting, rounding, displaying

Everything entering a program enters as text: a keyboard entry, a form field, a file column. The float() function pulls a number out of it, the mandatory step after an input, which always hands back a string. Without it, adding that text raises a TypeError; with it, a malformed entry raises a ValueError, which can be caught.

Once the number is in hand, three needs come up, each with its own tool.

PYTHON
entry = "19.99"
amount = float(entry)

int(amount)      # 19, the decimal part is cut off
round(amount)    # 20, rounded to the nearest
round(amount, 1) # 20.0, rounded but still a float

print(f"{amount:.2f} euros")
# 19.99 euros   the value in memory has not moved

The int() function truncates, round() genuinely rounds, and formatting inside an f-string touches the display only. That last distinction matters: rounding for the screen must never alter the value kept for the following calculations, or a total will drift away from the sum of its lines.

Warning

round() sends ties towards the even number: round(2.5) is 2, not 3. The rule avoids a bias over long series, but it catches out anyone comparing a Python total with a spreadsheet one.


Frequently asked questions

Question

Should float be used for amounts of money?

No, not as soon as accounting is involved. Gaps pile up across additions and end up producing a one-cent difference on an invoice, which is enough to get it rejected. Two options hold up: the decimal module, which computes in base ten, or amounts stored in cents, as whole numbers.

Question

What do inf and nan stand for?

float("inf") is infinity, produced by an overflow, and float("nan") means "not a number", the result of an undefined operation. Division by zero, on the other hand, raises a ZeroDivisionError rather than giving infinity. A quirk worth knowing while debugging: nan never equals itself.

Question

How can a value be checked for being a decimal number?

With isinstance(value, float), which answers with a bool. Watch out for the false negative: isinstance(3, float) is false, while 3 behaves like 3.0 in every calculation. To accept any number at all, testing both types together with isinstance(value, (int, float)) is the better move.

Related terms

Discover our python glossary

Browse the terms and definitions most commonly used in development with Python.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.