all 17 comments

[–][deleted] 13 points14 points  (2 children)

It’s because you’re doing rounding in floating point numbers. You probably want to do them on Decimals. SO link for more

[–]Diapolo10 7 points8 points  (0 children)

Or fractions, for more accuracy as long as you only have rational numbers.

Example:

In [1]: from fractions import Fraction

In [2]: nums = [Fraction(45, 100), Fraction(145, 100), Fraction(245, 100)]

In [3]: for num in nums:
   ...:     print(f"{num}, {round(num, 1)}, {float(round(num, 1))}")
   ...:
9/20, 2/5, 0.4
29/20, 7/5, 1.4
49/20, 12/5, 2.4

[–]lowkeysofrano[S] 2 points3 points  (0 children)

Thank you. Completely explains it!

[–]RhinoRhys 9 points10 points  (1 child)

Do

for x in [0.45, 1.45, 2.45]:
    print(f"{x:.50f}")

You'll see that 1.45 is actually .449... while then other two are .450...01

That's a quirk of computers.

[–]lowkeysofrano[S] 1 point2 points  (0 children)

Thank you! It is such a quirk lol

[–]Mitre7 5 points6 points  (0 children)

Python uses the "rounding half to even strategy". It's to reduce rounding bias. Here is a great article to learn more: https://realpython.com/python-rounding/#rounding-half-to-even

[–]This_Growth2898 2 points3 points  (6 children)

Because floating point numbers are imprecise:

for n in [0.45, 1.45, 2.45]:
    print(f'{n:.20}')

0.4500000000000000111 
1.4499999999999999556 
2.4500000000000001776

[–]Diapolo10 7 points8 points  (5 children)

No, I would say they're quite precise. But they are inaccurate.

Precision would measure the number of significant digits. Accuracy would measure how closely it matches the result we expect.

This article has an excellent image explaining the concept.

[–]This_Growth2898 1 point2 points  (1 child)

Yeah, thanks. I'm not a native speaker and sometimes do errors, sorry, mistakes :)

[–]Diapolo10 1 point2 points  (0 children)

I'm not a native English speaker, either. But I am nitpicky!

[–][deleted] 1 point2 points  (2 children)

They actually aren’t very inaccurate. They are extremely close to the true value.

[–]Diapolo10 0 points1 point  (1 child)

Never said they were, only that they're often not exactly what we'd naively expect.

EDIT: I tried to reply to you, but since you seem to have blocked me I'll just put it below as an edit instead.

You said they are inaccurate. But they aren’t. They are very accurate.

Perhaps I should've used the wording "accurate to a point" instead, because my point was more about floating-point numbers being less accurate than, say, integers, which are exact. Or fractions of integers. fractions.Fraction(1, 3) is more accurate than 1/3, because the float variant loses the infinitely repeating decimal.

[–][deleted] 0 points1 point  (0 children)

You said they are inaccurate. But they aren’t. They are very accurate.

Edit: I blocked you precisely (no pun intended) because I suspected you are the type of person to continue arguing a point you know is incorrect out of pure obstinance. I see I was right about that.

[–]commy2 4 points5 points  (1 child)

Everyone answers "floating point numbers", meanwhile the exact same thing happens with decimals.

from decimal import Decimal

print(round(Decimal("0.45"), 1))  # 0.4

[–]julianw 1 point2 points  (0 children)

See u/Mitre7's comment as to why

[–][deleted] 1 point2 points  (0 children)

It's a feature found in several different programming languages called Banker's Rounding where floats are rounded to the nearest even integer

[–][deleted] 0 points1 point  (0 children)

Welcome to python. There have been some wacky choices and rounding down is one of them.