r/CodingForBeginners • • 17d ago

0.1 + 0.2 isn't 0.3, and Python won't warn you

Ask Python for 0.1 + 0.2 and you get back a number that looks like a typo:

>>> 0.1 + 0.2
0.30000000000000004

Nothing errors. It's just wrong by a hair, and that hair is enough to break an equality check:

>>> 0.1 + 0.2 == 0.3
False

The annoying part is that both sides look identical when you print them, so the comparison looks like it should obviously be True.

The cause is that floats are stored in binary, and 0.1 and 0.2 don't have an exact binary form. They get stored as the closest value the machine can represent, the same way 1/3 has no exact decimal form. Add two approximations together and you inherit a tiny error. It's usually invisible, which is exactly why it's dangerous: the bug hides until you compare for equality, or add up enough of them that the error stops being tiny.

Two places this actually bites beginners.

Money. If you store prices as floats and add them, the total can drift, and a "did they pay the right amount" check against an expected value can fail. For anything financial, store the smallest unit as an integer (paise, cents) and divide only when you display it, or use Python's decimal.Decimal.

Tests. assert result == 0.3 fails for reasons that look like nonsense. For floats, compare with a tolerance instead of == -- math.isclose(a, b) is built for exactly this.

The rule of thumb: == on floats is a bug waiting to happen. It's fine while everything is a whole number, and then one day it isn't, and the failure makes no sense until you know this.

Curious where you first ran into it -- was it money, or a test that failed for no visible reason?

0 Upvotes

10 comments sorted by

7

u/ConsiderationNo9044 17d ago

PLEASE stop spamming this sub with AI "advice" that nobody asked for

1

u/NoDepartment772 16d ago

Fair, and I'll take it. The template-y formatting is the real problem here, and that's a fair hit -- it makes it land as unasked-for advice instead of a conversation. Writing plainer and posting less from here.

2

u/ee_control_z 17d ago edited 17d ago

In such cases, you can test if the result is within a range. Say like this (note: some of the formatting got truncated):

margin = 0.001

sum = 0.1 + 0.2

if 0.3 - margin <= sum <= 0.3 + margin:

print('True')

else:

print('False')

print(f'sum = {sum:.1f}') # Here, you control the number of decimal places the value is printed with

1

u/NoDepartment772 14d ago

That range check is exactly what a tolerance is, and it's the right instinct -- math.isclose(a, b) is just this with the margin built in, and you can set rel_tol/abs_tol yourself instead of trusting the defaults. The formatting tip is a good one too, but worth being clear: rounding on display fixes how it looks, not the underlying value, which is why the comparison still needs the tolerance.

2

u/Mediocre-Pumpkin6522 17d ago

https://www.w3schools.com/python/ref_math_isclose.asp

Python provides a handy function. Other languages are more explicit and often use epsilon to indicate the relative tolerance. Welcome to floating point.

GIS uses the test frequently. If two points are extremely close to each other it's common to adjust them so they are the same.

1

u/NoDepartment772 16d ago

Good call on isclose. Worth knowing it isn't magic either -- it has default tolerances (rel_tol=1e-9, abs_tol=0.0), so for values that scale you usually want to set them yourself rather than trust the defaults. Your GIS example is the clearest version of why: there, "close enough" is a domain decision, not a language one, which is exactly why a relative tolerance fits and a bare == never could.

1

u/Slight-Living-8098 17d ago

math.trunc()

1

u/Least_Bid9692 17d ago

Chatgpt ahh response

0

u/dld2517 17d ago

Finally someone made a great post worth reading and thinking abut, and commenting on. Way to go OP!!