-1

In my original code I was trying to compute some indices out of some float values and I faced the following problem:

>>> print int((1.40-.3)/.05)
21

But:

>>> print ((1.40-.3)/.05)
22.0

I am speechless about what is going on. Can somebody please explain?

Cupitor
  • 11,007
  • 19
  • 65
  • 91

3 Answers3

2

This is caused by floating point inaccuracy:

>>> print repr((1.40-.3)/.05)
21.999999999999996

You could try using the Decimal type instead:

>>> from decimal import Decimal
>>> Decimal
<class 'decimal.Decimal'>

and then

>>> (Decimal('1.40') - Decimal('.3')) / Decimal('.05')
Decimal('22')

The fractions.Fraction class would work too. Or, you could just round:

>>> round((1.40-.3)/.05, 10) #  round to 10 decimal places
22.0
tckmn
  • 57,719
  • 27
  • 114
  • 156
  • Although it's generally not the case that people *need* arbitrary precision. `21.999999999999996` is, for most practical purposes, `22`. – arshajii May 31 '14 at 13:42
  • for most practical purposes except for `int()` – Aprillion May 31 '14 at 13:43
  • `Decimal` works fine... except when it doesn't. For example, `1/d * d != 1` in general. It's only accurate when you stick to certain simple operations. –  May 31 '14 at 13:43
  • So does that mean that I need to cast everything in my code to `Decimal`?? – Cupitor May 31 '14 at 13:45
  • 1
    @Cupitor No, you could just round to 10 or so decimal places. – tckmn May 31 '14 at 13:46
  • @Doorknob, I am actually reading the values from a file and retrieve them with `eval`. I cannot apply rounding or Decimal on eval. How can I correct this problem? – Cupitor May 31 '14 at 14:02
  • 1
    @Cupitor `eval`? I hope that file isn't from user input, and I hope it couldn't be edited by a malicious user. I'd recommend stopping that immediately and using `float` (as in `float("12.34")`) and rounding instead. – tckmn May 31 '14 at 14:06
  • Ha ha! Thanks for the headsup. No this is a completely offline scientific program. But I didn't know that about float. Thanks. – Cupitor May 31 '14 at 14:07
1

Drop the print and you'll see that the actual value is:

>>> (1.40-.3)/.05
21.999999999999996

Python 2 print() (more accurately, float.__str__) lies to you by rounding to a couple of decimal digits. Python 3 print() (again, actually float.__str__) doesn't do that, it always gives a faithful representation of the actual value (it abbreviates, but only when it doesn't change the value).

This inaccuracy is inherent to floating point numbers (including Decimal, though its inaccuracies occur different cases). This is a fundamental problem, representing arbitrary real numbers is not possible. See Is floating point math broken? for explanations.

Community
  • 1
  • 1
  • I see. But the "actual" value is 22.0! – Cupitor May 31 '14 at 13:42
  • @Cupitor What do you mean by "actual"? –  May 31 '14 at 13:44
  • I mean the real(mathematical) value of the expression. – Cupitor May 31 '14 at 13:46
  • @Cupitor you obviously haven't read the article from the first comment under your question. Real numbers cannot be represented by computers, only a subset of rational numbers. – Aprillion May 31 '14 at 13:48
  • @deathApril, no I didn't. I understand that computers can't. You wrote that `print` function lies by saying 22.0. I was just pointing out well in this case it is not a lie :) – Cupitor May 31 '14 at 13:49
  • 1
    @Cupitor It is a lie with regards to the value the program is actually dealing with. The rounding hides a very real error/inaccuracy in this case, but that doesn't make the error go away and in other cases it increases the error (e.g. for 1.0/3). –  May 31 '14 at 13:52
  • Yes that is true. Thanks anyways. I am quite happy to learn this about print(). – Cupitor May 31 '14 at 13:53
0

I think this explains it straightforwardly:

import decimal

>>> (decimal.Decimal(1.40) -decimal.Decimal(.3))/decimal.Decimal(.05)
Decimal('21.99999999999999722444243843')
>>> (decimal.Decimal('1.40') -decimal.Decimal('.3'))/decimal.Decimal('.05')
Decimal('22')
Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331