6
>>> sum([0.3, 0.1, 0.2])
0.6000000000000001

>>> sum([0.3, 0.1, 0.2]) == 0.6
False

What can I do to make the result be exactly 0.6? I don't want to round the result to a certain number of decimal digits because then I could lose precision for other list instances.

user2460978
  • 735
  • 6
  • 19
  • Why do you need exactly 0.6? Would you have the same problem if `1.0/3+2.0/3` did not exactly equal `1`? – user2357112 Mar 09 '14 at 14:47
  • I want exactly 0.6 because that is the result I'd expect. Yes, it, would be the same problem. – user2460978 Mar 09 '14 at 14:49
  • 2
    There are plenty of posts on this on SO already, e.g.: http://stackoverflow.com/questions/5595425/what-is-the-best-way-to-compare-floats-for-almost-equality-in-python – Se Norm Mar 09 '14 at 14:50
  • 1
    @user2460978 I'm afraid the answer is to adjust your expectations. There isn't a good general-purpose way to have math with rationals work as you'd expect while still permitting things like square roots and sines. – user2357112 Mar 09 '14 at 14:55
  • Welcome to floating point. 0.6 cannot be exactly represented in binary floating point. – Fred Larson Mar 09 '14 at 14:56
  • but 0.6 + 0 returns 0.6 – user2460978 Mar 09 '14 at 14:59
  • @user2460978 When you write `0.6`, this is turned into a base 2 *approximation* of the rational number 6/10. Adding zero to it doesn't change that number. Then you print said approximation, and Python conveniently (or confusingly, depending on your point of view) hides the approximation by rounding it into 0.6 which, if put back into Python code, would *again* turn into the same approximation of 6/10. At no point Python actually stored the real number 0.6 or the rational 6/10. –  Mar 09 '14 at 15:19
  • @delnan well, my question is exactly why it 'hides', as you say, the approximation in the case 0.6+0 but not in the case 0.1+0.2+0.3 – user2460978 Mar 10 '14 at 15:14
  • @user2460978 The value resulting from evaluating `0.60+` (or `0.6` because +0 doesn't change anything) can be reproduced by parsing the string `"0.6"`. The value resulting from evaluating `0.1+0.2+0.3` cannot be reproduced by parsing `0.6`, because the compound rounding errors in parsing "0.1", "0.2", "0.3" and adding them is different from (and larger than) the rounding error in parsing "0.6". See http://babbage.cs.qc.cuny.edu/IEEE-754/ for exploring this interactively. Perhaps "hide" is the wrong term, is seeks the shortest decimal string that reproduces the bits stored when parsed. –  Mar 10 '14 at 16:06

2 Answers2

6

A float is inherently imprecise in pretty much every language because it cannot be represented precisely in binary.

If you need exact precision use the Decimal class:

from decimal import Decimal

num1 = Decimal("0.3")
num2 = Decimal("0.2")
num3 = Decimal("0.1")

print(sum([num1, num2, num3]))

Which will return the very pleasing result of:

Decimal('0.6')  # One can do float() on this output to get plain (0.6).

Which conveniently is also a Decimal object with which you can work.

anon582847382
  • 19,907
  • 5
  • 54
  • 57
  • 2
    `Decimal` is also a floating point number, just using a different base. It can represent the literals one writes in decimal notation exactly, but in turn is less accurate than binary ("ordinary") floats in other cases. –  Mar 09 '14 at 15:15
  • Interesting. When you say 'using a different base', I assume it's base 10? – user2460978 Mar 09 '14 at 15:17
  • 2
    @user2460978 Exactly. Instead of {some integer} * 2^x (that's what a `float` looks like), a `decimal` is stored as {some integer} * 10^x. –  Mar 09 '14 at 15:20
1

Use math.fsome() instead of sum().

4b0
  • 21,981
  • 30
  • 95
  • 142
Kishore
  • 11
  • 1
  • 1
    I guess you mean [`math.fsum`](https://docs.python.org/3/library/math.html#math.fsum) rather than `math.fsome`? (There's no such thing as `math.fsome` in Python.) But even then, it's not really a solution to the problem: try `math.fsum([0.1, 0.2, 0.4])`, for example. On a typical machine, the result won't be equal to `0.7`. – Mark Dickinson Aug 30 '18 at 18:18