1
>>> num = 4.123456
>>> round(num, 3) # expecting 4.123
4.1230000000000002

I'm expecting 4.123 as a result, Am I wrong?

mskfisher
  • 3,291
  • 4
  • 35
  • 48
pocoa
  • 4,197
  • 9
  • 37
  • 45
  • *(related)* http://stackoverflow.com/questions/56820/round-in-python-doesnt-seem-to-be-rounding-properly – Felix Kling May 21 '10 at 08:35
  • Also very related: http://stackoverflow.com/questions/1089018/why-cant-decimal-numbers-be-represented-exactly-in-binary – Joren May 21 '10 at 08:42
  • 1
    It's a FAQ: http://www.python.org/doc/faq/general/#why-are-floating-point-calculations-so-inaccurate – dan04 May 24 '10 at 23:38

4 Answers4

7

This is not a mistake. You need to read What Every computer Scientist Should Know About Floating Point Arithmetic:

http://docs.sun.com/source/806-3568/ncg_goldberg.html

ptomato
  • 56,175
  • 13
  • 112
  • 165
6

Yep, your expectations don't match the design intent of your tools.

Check out this section of the Python tutorial.


Using math.round is actually pretty rare. if you're trying to display a number as a string to a certain precision, you might want something more like

>>> num = 4.123456
>>> print "%.3f" % num
4.123

You might be interested in the documentation on string formatting.

Mike Graham
  • 73,987
  • 14
  • 101
  • 130
  • 1
    I'm finding the canon floating point page over-linked. The link you gave in Python docs is way more practical, and on a level most people can understand and appreciate. – Xavier Ho May 21 '10 at 08:41
  • Both are valuable resources. I agree this one is a lot better for beginners, but eventually people will want to read the Goldberg article as well. – Joren May 21 '10 at 08:46
  • Thanks. But I just want to get that as a number, not string. – pocoa May 21 '10 at 08:57
  • 4
    @pocoa, What is wrong with `4.1230000000000002`? If you think `decimal.Decimal` is the answer, you are most likely not understanding the situation well enough. – Mike Graham May 21 '10 at 09:13
  • 1
    maybe you're right. I just wanted to store the first 3 digits. I could have the same effect with doing string formatting when displaying it. I was just confused. – pocoa May 21 '10 at 10:50
4

Why do you care? (That's a serious question.)

The answer that you're getting is so close to 4.123 as to make no difference. It can't be exactly 4.123, since there are only finitely many numbers (around 2**64 on a typical machine) that Python can represent exactly, and without going into detail about floating-point representations, it just so happens that 4.123 isn't one of those numbers. By the way, 4.1230000000000002 isn't one of the numbers that can be exactly represented, either; the actual number stored is 4.12300000000000022026824808563105762004852294921875, but Python truncates the decimal representation to 17 significant digits for display purposes. So:

  1. If you're doing mathematics with the result, then the difference between 4.123 and what you're getting is so tiny as to make no real difference. Just don't worry about it.
  2. If you just care about the output looking pretty (i.e., what you're after here is a string rather than a number) then use str, or string formatting.
  3. In the unlikely case that the difference really does matter, e.g., because you're doing financial work and this affects the direction that something rounds later on, use the decimal module.

Final note: In Python 3.x and Python 2.7, the repr of a float has changed so that you will actually get 4.123 as you expect here.

Mark Dickinson
  • 29,088
  • 9
  • 83
  • 120
  • 1
    +1, "If you're doing mathematics with the result, then the difference between `4.123` and what you're getting is so tiny as to make no real difference." <--- exactly – Mike Graham May 21 '10 at 09:15
  • Yes, you're right. Thank you guys for making me understand/remember that. – pocoa May 21 '10 at 10:52
2

If you want to have an exact representation of your floating point number, you have to use decimal.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 4
    Decimal is in general no more exact than binary floating point. It can represent *decimal* fractions exactly (up to a point), which is of course the entire point of the data type, but it also has its limitations. – Joren May 21 '10 at 08:39
  • 3
    `decimal.Decimal` has both representation and roundoff errors, just like `float`, though you can control them some additional ways. I have yet to see a case where someone's problem of float inexactness had the best solution of using `Decimal` instead. `Decimal` is mostly useful in niche financial applications. – Mike Graham May 21 '10 at 08:43
  • @Joren: *Compared to the built-in float implementation of binary floating point, the new class is especially helpful for financial applications and other uses which require **exact decimal representation**, control over precision, control over rounding to meet legal or regulatory requirements, tracking of significant decimal places, or for applications where the user expects the results to match calculations done by hand.* from [this book](http://www.network-theory.co.uk/docs/pytut/DecimalFloatingPointArithmetic.html). Maybe my answer is not well phrased but this is what I meant. – Felix Kling May 21 '10 at 08:47
  • 2
    "Exact decimal representations" (which slightly oversells `decimal.Decimal` to start with) is a lot different than "*exact* floating point numbers". The key phrase in that quote is "helpful for financial applications". – Mike Graham May 21 '10 at 08:49
  • 1
    @Felix: What Mike says. For what it's worth, that downvote wasn't mine, I think your suggestion can be helpful for dealing with the OP's problem. But I needed to remark that 'exact floating point' is misleading. Decimal floating point will not represent, for example, 1/3 exactly. For that you'd need to have ternary floating point. There is an infinite amount of other fractions decimal floating point can't represent exactly. – Joren May 21 '10 at 09:43
  • @Joren: Ok. I have to confess I am not that much into floating point numbers so my wording was probably wrong. Sorry for that. Anyway, as you said, this module might help the OP with his problem. – Felix Kling May 21 '10 at 09:49