Limiting floats to two decimal points

4,539,325

Solution 1

You are running into the old problem with floating point numbers that not all numbers can be represented exactly. The command line is just showing you the full floating point form from memory.

With floating point representation, your rounded version is the same number. Since computers are binary, they store floating point numbers as an integer and then divide it by a power of two so 13.95 will be represented in a similar fashion to 125650429603636838/(2**53).

Double precision numbers have 53 bits (16 digits) of precision and regular floats have 24 bits (8 digits) of precision. The floating point type in Python uses double precision to store the values.

For example,

>>> 125650429603636838/(2**53)
13.949999999999999

>>> 234042163/(2**24)
13.949999988079071

>>> a = 13.946
>>> print(a)
13.946
>>> print("%.2f" % a)
13.95
>>> round(a,2)
13.949999999999999
>>> print("%.2f" % round(a, 2))
13.95
>>> print("{:.2f}".format(a))
13.95
>>> print("{:.2f}".format(round(a, 2)))
13.95
>>> print("{:.15f}".format(round(a, 2)))
13.949999999999999

If you are after only two decimal places (to display a currency value, for example), then you have a couple of better choices:

  1. Use integers and store values in cents, not dollars and then divide by 100 to convert to dollars.
  2. Or use a fixed point number like decimal.

Solution 2

There are new format specifications, String Format Specification Mini-Language:

You can do the same as:

"{:.2f}".format(13.949999999999999)

Note 1: the above returns a string. In order to get as float, simply wrap with float(...):

float("{:.2f}".format(13.949999999999999))

Note 2: wrapping with float() doesn't change anything:

>>> x = 13.949999999999999999
>>> x
13.95
>>> g = float("{:.2f}".format(x))
>>> g
13.95
>>> x == g
True
>>> h = round(x, 2)
>>> h
13.95
>>> x == h
True

Solution 3

The built-in round() works just fine in Python 2.7 or later.

Example:

>>> round(14.22222223, 2)
14.22

Check out the documentation.

Solution 4

Nobody here seems to have mentioned it yet, so let me give an example in Python 3.6's f-string/template-string format, which I think is beautifully neat:

>>> f'{a:.2f}'

It works well with longer examples too, with operators and not needing parens:

>>> print(f'Completed in {time.time() - start:.2f}s')

Solution 5

I feel that the simplest approach is to use the format() function.

For example:

a = 13.949999999999999
format(a, '.2f')

13.95

This produces a float number as a string rounded to two decimal points.

Share:
4,539,325
Mateen Ulhaq
Author by

Mateen Ulhaq

Elen síla lúmenn' omentielvo. Favorite languages: Python, Rust, Kotlin, C++, C#, Haskell Photography

Updated on July 08, 2022

Comments

  • Mateen Ulhaq
    Mateen Ulhaq almost 2 years

    I want a to be rounded to 13.95. I tried using round:

    >>> a
    13.949999999999999
    >>> round(a, 2)
    13.949999999999999
    
    • Admin
      Admin over 15 years
    • Admin
      Admin over 15 years
    • SingleNegationElimination
      SingleNegationElimination about 15 years
      Hmm... Are you trying to represent currency? If so, you should not be using floats for dollars. You could probably use floats for pennies, or whatever the smallest common unit of currency you're trying to model happens to be, but the best practice is to use a decimal representation, as HUAGHAGUAH suggested in his answer.
    • Davoud Taghawi-Nejad
      Davoud Taghawi-Nejad almost 12 years
      It is important not to represent currency in float. Floats are not precise. But penny or cent amounts are integers. Therefore integers are the correct way of representing currency.
    • Basic
      Basic about 11 years
      @DavoudTaghawi-Nejad or more to the point... The Decimal Type
    • Davoud Taghawi-Nejad
      Davoud Taghawi-Nejad about 10 years
      @Basic, it depends(mostly no). Using integers in cents, or pennies is fool prove. Its the industry standard of representing money. If you know what you are doing, have a sound understanding of floating point arithmetic and python's decimal class, you might use decimal. But it depends much of your problem. Do you need arbitrary precision decimals? Or only two digits? If two digits: integer. It keeps you out of trouble. Source I worked in a software consultancy for banking.
    • bad_keypoints
      bad_keypoints over 8 years
      I'm coming probably too late here, but I wanted to ask, have the developers of Python solved this problem? Because when I do round(13.949999999999999, 2), I simply get 13.95. I've tried it in Python 2.7.6, as well as 3.4. It works. Not sure if 2.7 even was there in 2009. Maybe it's a Python 2.5 thing?
    • hynekcer
      hynekcer almost 8 years
      @bad_keypoints: Yes, the rounding problem has been solved by by Python 2.7.0+. More in my answer here
    • gerrit
      gerrit about 7 years
      @DavoudTaghawi-Nejad penny or cent amounts are integers, not for petrol prices...
    • Davoud Taghawi-Nejad
      Davoud Taghawi-Nejad about 7 years
      They are still integers, but with a lower base value. 0.5 cent for example would be 500 of denomination 1/1000 cent.
    • Palak Bansal
      Palak Bansal over 3 years
      Its working for me on python 2.7. Must have been updated
  • Christian
    Christian over 11 years
    But, what about when the number is going from 13.95 to let's say 13.90 ? My output will then be 13.9 I would like it to show the zero
  • Basic
    Basic about 11 years
    @Christian There's a fundamental difference between the value stored and how you display that value. Formatting the output should allow you to add padding as required, as well as adding comma separators, etc.
  • lifebalance
    lifebalance over 10 years
    But be cautioned, value of a is still an imprecise float. Take a look here - repl.it/LJs (Click "Run Session" on the top of the Right section).
  • andilabs
    andilabs over 10 years
    worth mention that "%.2f" % round(a,2) you can put in not only in printf, but also in such things like str()
  • arhuaco
    arhuaco over 10 years
    If you go with this approach, you should add a 0.5 for a more accurate representation. int(a * 100 + 0.5) / 100.0 ; Using math.ceil is another option.
  • skytux
    skytux over 10 years
    How do you represent an integer? If I use "{i3}".format(numvar) I get an error.
  • skytux
    skytux over 10 years
    This is what I mean: If numvar=12.456, then "{:.2f}".format(numvar) yields 12.46 but "{:2i}".format(numvar) gives an error and I'm expecting 12.
  • worc
    worc over 10 years
    why is it that people always assume currency on floating-point rounding? sometimes you just want to work with less precision.
  • sam-6174
    sam-6174 almost 10 years
    It should be noted that "%.2f" %0.245 will output '0.24', i.e. it only rounds up on 6 or greater
  • Shashank Sawant
    Shashank Sawant almost 10 years
    If a member is going to down vote this answer, please provide an explanation, so that we can understand what's wrong with this approach. For the reason mentioned by OriolJ, I find this to be the best answer.
  • John Y
    John Y almost 10 years
    @user2426679: You seem not to have understood this answer. Try rounding 0.225 to two decimal places.
  • John Y
    John Y almost 10 years
    @ShashankSawant: Well, for one thing, the answer as presented does not round, it truncates. The suggestion to add half at the end will round, but then there is no benefit to doing this over just using the round function in the first place. For another thing, because this solution still uses floating point, the OP's original problem remains, even for the "corrected" version of this "solution".
  • Stephen Blum
    Stephen Blum almost 10 years
    to add commas as well you can '{0:,.2f}'.format(1333.949999999) which prints '1,333.95'.
  • Jossef Harush Kadouri
    Jossef Harush Kadouri over 9 years
    @OnurYıldırım: yes, but you can wrap it with float(); float("{0:.2f}".format(13.9499999))
  • yantrab
    yantrab over 9 years
    @JossefHarush you can wrap it with float(), but you haven't gained anything. Now you have a float again, with all the same imprecision. 13.9499999999999 and 13.95 are the same float.
  • Jossef Harush Kadouri
    Jossef Harush Kadouri over 9 years
    @NedBatchelder: i agree that they are equal, but this limits the float to two decimal points :)
  • interjay
    interjay over 9 years
    -1, this is just an unnecessary reimplementation of the round function (which was used in the question).
  • Pithikos
    Pithikos about 9 years
    @interjay which is necessary if the round() doesn't work as the OP mentioned.
  • Alex Punnen
    Alex Punnen over 8 years
    Note In the format 0 is positional argument not needed in python 2.7 or 3.1 , I thought it was part of format and got strange results :) ("{:.2f}".format(a)
  • vaultah
    vaultah over 8 years
    Please don't post identical answers to multiple questions.
  • Angry 84
    Angry 84 over 8 years
    WOW... tdh... Please never make any accounting software... What happens if the number happen to be 113.94 ?? this would result in 113.9 ... leaving 0.04 missing.... Also this already has answers from over 5 years ago....
  • szeitlin
    szeitlin about 8 years
    doesn't work for me on python 3.4.3 and numpy 1.9.1 ? >>> import numpy as np >>> res = 0.01 >>> value = 0.184 >>> np.round(value/res) * res 0.17999999999999999
  • iblasi
    iblasi about 8 years
    Looking for documentation I see the problem comes from numpy.round accuracy/precision. So it requires to define it as int before multiplication with resolution. I updated the code. Thank you for that!
  • hynekcer
    hynekcer about 8 years
    Why downvoted? The question was about Python float (double precision) and normal round, not about numpy.double and its conversion to string. Plain Python rounding really can not be done better than in Python 2.7. The most of answers has been written before 2.7, but they are obsoleted, though they were very good originally. This is the reason of my answer.
  • hynekcer
    hynekcer about 8 years
    The only necessary is to convert numpy.float64 result of np.round to float or simply to use round(value, 2). No valid IEEE 754 number exists between 13.949999999999999 (= 1395 / 100.) and 3.950000000000001 (= 1395 * .01). Why do you think that your method is the best? The original value 13.949999999999999289 (= value = round(value, 2)) is even more exact than your 13.95000000000000178 (printed by np.float96). More info also for numpy is now added to my answer that you probably downvoted by mistake. It wasn't about numpy originally.
  • iblasi
    iblasi about 8 years
    @hynekcer I do not think that my answer is the best. Just wanted to add an example of limit float to n decimals but the nearest of a defined resolution. I checked as you said, that instead of intyou can also use floatfor @szeitlin example. Thank you for your extra comment. (Sorry but I did not downvote you)
  • radtek
    radtek almost 8 years
    @JohnY why then 0.245 rounds to 0.24 while 0.225 rounds correctly? I would like to see 0.245 round to 0.25
  • John Y
    John Y almost 8 years
    @radtek: You need to understand that the binary value (of type float) is just the closest available approximation of the decimal number (that you are familiar with as a human being). There is no such (finitely representable) binary value as 0.245. It simply does not exist, and mathematically cannot exist. The binary value which is closest to 0.245 is slightly less than 0.245, so naturally it rounds down. Likewise, there is no such thing as 0.225 in binary, but the binary value which is closest to 0.225 is slightly greater than 0.225, so naturally it rounds up.
  • radtek
    radtek almost 8 years
    @JohnY I wasn't asking for an explanation, just a solution. I think the only solution is to use Decimal type, unless someone can name a better one.
  • John Y
    John Y almost 8 years
    @radtek: You did literally ask for an explanation. The most straightforward solution is indeed to use Decimal, and that was one of the solutions presented in this answer. The other was to convert your quantities to integer and use integer arithmetic. Both of these approaches also appeared in other answers and comments.
  • Rick James
    Rick James almost 7 years
    53 bits when you include the "hidden bit", which is implicitly 1, except during "gradual underflow".
  • Rick James
    Rick James almost 7 years
    It's not round's fault, it's the display fault.
  • hynekcer
    hynekcer almost 7 years
    Yes, it's well known. I miss however a context if you object to something in Python 2.7 Release notes or in my text or to nothing at all. It is more complicated than was necessary the purpose of this question. It should be added that also conversion from string to float has been fixed in Python 2.7 due to rounding bug on certain 32-bit Intel chips and that "The round() function is also now correctly rounded." (Release notes - 3.1 features backported to 2.7). Can you agree?
  • Rick James
    Rick James almost 7 years
    Oops, that was a*b vs b*a. Thanks for the links -- Nostalgia.
  • StartupGuy
    StartupGuy over 6 years
    So am I to understand that this is a Python 2.7 fail? Why would such a fundamental function yield different results from v 2.7 to v 3?
  • Julio Marins
    Julio Marins over 6 years
    getcontext().prec = 6 works for just the scope of the function or all places?
  • Siamand
    Siamand over 6 years
    Contexts are environments for arithmetic operations. They govern precision, set rules for rounding, determine which signals are treated as exceptions, and limit the range for exponents. Each thread has its own current context @JulioMarins
  • Neil
    Neil over 6 years
    There's no way to "round" a number. Formatting is just for pretty-printing. If you really need to store the digits (e.g. keeping track of money) use integers.
  • Xolve
    Xolve over 6 years
    @NeilChowdhury True, I asked me same after writing the answer. Assumption of question is to display float where to two decimal places.
  • AlejandroVD
    AlejandroVD over 6 years
    Notice that if you want to format more than one number, you need to increment the integer before the colon: >>> print("{0:.2f} - {0:.2f}".format(.1234, .5678)) 0.12 - 0.12 >>> print("{0:.2f} - {1:.2f}".format(.1234, .5678)) 0.12 - 0.57
  • jiamo
    jiamo over 6 years
    but round(2.16, 1) give 2.2 why python just offer a truncate func
  • Mark Dickinson
    Mark Dickinson over 6 years
    This is nonsense. The two statements given behave identically on Python 2.7, and only the second statement is valid on Python 2.6. (Neither statement is valid in Python 3 or Python < 2.6.) The first form has no advantage besides brevity.
  • Alexey Antonenko
    Alexey Antonenko over 6 years
    I mean, print"{0:.2f} {0:.2f}".format(a, b) will lead to mistake in output - it will output 'a' value twice. While print"{:.2f} {:.2f}".format(a, b) will output 'a' and 'b' values.
  • Alexey Antonenko
    Alexey Antonenko over 6 years
    For Python 3, you just need to add brackets print(...). And within them all I wrote is right.
  • Mark Dickinson
    Mark Dickinson over 6 years
    "I mean, print"{0:.2f} {0:.2f}".format(a, b) will lead to mistake in output ". Ah. Well, that's quite a different statement! Maybe you should edit your answer? (What does "raise error" mean in the current answer, for example? Can you give an example of a case where the second statement raises an exception but the first doesn't?)
  • Hovo
    Hovo about 6 years
    You would be after print("{0:.2f} {1:.2f}".format(a, b)) if you have two variables
  • duhaime
    duhaime about 6 years
    This works fine in Python 3, you just need to make sure you include the .0 in the denominator
  • Jérôme
    Jérôme about 6 years
    The sample code fails for both Python 2.7.13 and Python 3.5.3.
  • hynekcer
    hynekcer about 6 years
    @Jérôme Thanks. Fixed now. I was a typo x instead of y when I combined two tests into this example for different order of magnitude.
  • Mark Dickinson
    Mark Dickinson almost 6 years
    This doesn't help at all. output has the exact same value as a, so you might as well have written print a instead of print output in the last line.
  • Shashank Singh
    Shashank Singh over 5 years
    @MarkDickinson Could you please try again. Because It is running as expected in my compiler.
  • Mark Dickinson
    Mark Dickinson over 5 years
    You're missing my point. Yes, your code prints 13.95. But so does print a, for this particular value of a, in Python 2.7, so it's not really clear what the point of the formatting step was.
  • Shashank Singh
    Shashank Singh over 5 years
    @MarkDickinson I have edited the code. I agree that 'print a' does print the same value as "print output". But if you compare "a==output", the result will be "False" because formatting step does round off the floating value "a" to two decimal points.
  • Mark Dickinson
    Mark Dickinson over 5 years
    Did you actually try a == output for the code you show? It gives True for me, and I suspect it does for you, too.
  • Shashank Singh
    Shashank Singh over 5 years
    @MarkDickinson For a=13.949999999999999, I am getting True. For a=13.9499999, I am getting False. I am a bit confused now!!!
  • Melroy van den Berg
    Melroy van den Berg over 5 years
    For example, if you try to round the value 2.675 to two decimal places, you get this >>> round(2.675, 2) 2.67 docs.python.org/2/tutorial/floatingpoint.html
  • Andrey Semakin
    Andrey Semakin about 5 years
    By the way, since Python 3.6 we can use f-strings: f"Result is {result:.2f}"
  • Andrey Semakin
    Andrey Semakin about 5 years
    By the way, since Python 3.6 we can use f-strings: f"Result is {result:.2f}"
  • Jemshit Iskenderov
    Jemshit Iskenderov about 5 years
    This returns string
  • Hejazzman
    Hejazzman about 5 years
    Adding whole new dependency for numeric processing (pandas) is the "best way"?
  • Richard Dally
    Richard Dally almost 5 years
    From Python 3 documentation page: Note The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float.
  • PV8
    PV8 almost 5 years
    how does this one work, if I want to add a string in front for example: print('blabla floatnumber function(x))
  • Josh Correia
    Josh Correia almost 5 years
    Note that if you try to use this method to print out a number such as 1.00000 it will only print out 1.0, regardless of how many decimal points you specify.
  • Mahdi Amrollahi
    Mahdi Amrollahi over 4 years
    I could not understand correctly why the python store a number and then divide it by another number instead of storing the real value of the number? Why do not we have such a thing in c++ or c# or ...? Why cannot we store 0.0001_0000_0000_0000... for 0.1?
  • Mark Ransom
    Mark Ransom over 4 years
    @Pithikos round() works just fine, and this code doesn't work any better. The problem is that Python uses binary floating point numbers, and the closest you can get to 13.95 is 13.949999999999999. If you see code that generates any other number, it's because more rounding is applied when converting to a string. See Is floating point math broken?.
  • Gustavo Kawamoto
    Gustavo Kawamoto over 3 years
    The question specifically says that the round method does not do what he wants. Check this answer for more info on why is that
  • galactica
    galactica over 3 years
    it's late 2020 and this is still so much down below
  • Naman Jain
    Naman Jain over 3 years
    But how did you get an additional 0 to show up if we have say, 0.093. This is giving me 0.1 as the answer
  • Irfan wani
    Irfan wani over 3 years
    If you return 2 decimal places or in general 1 place more than the number of zeroes present in the decimal part on left side, then you will get the correct result as you want.e.g, if you apply simply my answer to the number 0.093, it will return 0.09 but if you want to get only 1 decimal place, then of course, it will return 0.1 as 0.0 is completely wrong.(and my code works in the same way. Maybe you want to get only 1 decimal place. If you want to get more accurate results, you must have to increase the number of decimal places.)
  • ShadowRanger
    ShadowRanger over 3 years
    @MahdiAmrollahi: Computers have bits. They can't store "real numbers". They just put bits together and interpret them in conventional ways. For integers, it's fairly easy (many bits are a number in base 2). But with only 0 and 1 you can't store "the real value", especially if the real value is an irrational value (e.g. pi). IEEE-754 arithmetic (float in Python, float/double in C/C++/C#) is efficient and "close enough". More precise options exist (fractions.Fraction, decimal.Decimal; the latter uses a storage format similar to what you're asking for), but they're slower and bigger.
  • Md. Sabbir Ahmed
    Md. Sabbir Ahmed over 3 years
    @RichardDally Thanks for the much needed information. Can you please explain why "most decimal fractions can’t be represented exactly as a float"?
  • Arar
    Arar about 3 years
    It's not true that it will return decimal, according to the documentation ` The return value is an integer if ndigits is omitted or None. Otherwise the return value has the same type as the number. ndigits may be negative.`
  • ARNON
    ARNON about 3 years
    The best, by far.
  • Arpan Saini
    Arpan Saini almost 3 years
    if we just want to truncate, how to get that.
  • aamarks
    aamarks almost 3 years
    All these bogus up votes. Your answer just repeats the code the OP posted when asking the question a decade ago. The OP knows the round function. You didn't solve his problem with it at all. (The problem doesn't even exist today.)
  • alper
    alper almost 3 years
    Instead of Decimal can we return float? like: def round_float(v, ndigits=2) -> float: d = Decimal(v); v_str = ("{0:.%sf}" % ndigits).format(round(d, ndigits)); return float(v_str)
  • weaming
    weaming almost 3 years
    @alper you can return whatever you prefer to
  • User
    User over 2 years
    I can't repeat the result. round(a,2) gets 13.95 by python 2.7 and 3.7.
  • Rex Logan
    Rex Logan over 2 years
    Yes the newer versions of 2.7 and 3.x have auto rounding when they display as a string. This was not the case 14 years ago
  • Eric
    Eric over 2 years
    This doesn't work for larger numbers. Round doesn't behave like what people want. We want a decimal format not a round. 99.9999999987 should not turn into 100 it should be 99.99. That is the issue people want resolved. Simple small math is a no brainer.
  • Martin Konicek
    Martin Konicek over 2 years
    The answer I was looking for.
  • Steve Maguire
    Steve Maguire about 2 years
    I tried to use this with a column of a DateFrame. got the message: TypeError: conversion from Series to Decimal is not supported
  • Ray Tayek
    Ray Tayek about 2 years
    seems like two '*' in a row disappears.
  • Ray Tayek
    Ray Tayek about 2 years
  • Mark Dickinson
    Mark Dickinson about 2 years
    There's a lot of unnecessary stuff here. 8/3 is already of type float, so the float call does nothing useful. And calling dunder methods directly is a bit odd - instead, simply call the function that delegates to those dunder methods. So a better way to spell the first line is simply value = round(8/3, 2). And at that point you're not really adding anything that isn't already in other answers.
  • Praveen Kumar
    Praveen Kumar about 2 years
    Sorry for the irrelevant answer. I thought it is the correct method. Also, In the question section, he mentioned that the round method was not working and so I didn't check it.