By swilliams


2008-09-11 15:08:09 8 Comments

The documentation for the round() function states that you pass it a number, and the positions past the decimal to round. Thus it should do this:

n = 5.59
round(n, 1) # 5.6

But, in actuality, good old floating point weirdness creeps in and you get:

5.5999999999999996

For the purposes of UI, I need to display 5.6. I poked around the Internet and found some documentation that this is dependent on my implementation of Python. Unfortunately, this occurs on both my Windows dev machine and each Linux server I've tried. See here also.

Short of creating my own round library, is there any way around this?

17 comments

@Frank Krueger 2008-09-11 15:12:19

You can switch the data type to an integer:

>>> n = 5.59
>>> int(n * 10) / 10.0
5.5
>>> int(n * 10 + 0.5)
56

And then display the number by inserting the locale's decimal separator.

However, Jimmy's answer is better.

@Jimmy 2008-09-11 15:11:41

I can't help the way it's stored, but at least formatting works correctly:

'%.1f' % round(n, 1) # Gives you '5.6'

@mychll 2015-07-15 03:56:07

i tried print '%.2f' % 655.665 but it returns 655.66, it should be 655.67

@Jimmy 2015-07-15 22:42:30

@Kyrie see stackoverflow.com/questions/9301690/…. Floating point inaccuracy is to blame here -- "5.665 -> 5.67" but "15.665 -> 15.66". Use decimals if you need exact precision.

@mychll 2015-07-16 01:22:04

this is working after searching :) from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN # use in rounding floating numbers Decimal(str(655.665)).quantize(Decimal('1.11'), rounding=ROUND_HALF_UP) # Issues and Limitations in floating points

@Syed Is Saqlain 2018-04-11 14:05:40

The problem is only when last digit is 5. Eg. 0.045 is internally stored as 0.044999999999999... You could simply increment last digit to 6 and round off. This will give you the desired results.

import re


def custom_round(num, precision=0):
    # Get the type of given number
    type_num = type(num)
    # If the given type is not a valid number type, raise TypeError
    if type_num not in [int, float, Decimal]:
        raise TypeError("type {} doesn't define __round__ method".format(type_num.__name__))
    # If passed number is int, there is no rounding off.
    if type_num == int:
        return num
    # Convert number to string.
    str_num = str(num).lower()
    # We will remove negative context from the number and add it back in the end
    negative_number = False
    if num < 0:
        negative_number = True
        str_num = str_num[1:]
    # If number is in format 1e-12 or 2e+13, we have to convert it to
    # to a string in standard decimal notation.
    if 'e-' in str_num:
        # For 1.23e-7, e_power = 7
        e_power = int(re.findall('e-[0-9]+', str_num)[0][2:])
        # For 1.23e-7, number = 123
        number = ''.join(str_num.split('e-')[0].split('.'))
        zeros = ''
        # Number of zeros = e_power - 1 = 6
        for i in range(e_power - 1):
            zeros = zeros + '0'
        # Scientific notation 1.23e-7 in regular decimal = 0.000000123
        str_num = '0.' + zeros + number
    if 'e+' in str_num:
        # For 1.23e+7, e_power = 7
        e_power = int(re.findall('e\+[0-9]+', str_num)[0][2:])
        # For 1.23e+7, number_characteristic = 1
        # characteristic is number left of decimal point.
        number_characteristic = str_num.split('e+')[0].split('.')[0]
        # For 1.23e+7, number_mantissa = 23
        # mantissa is number right of decimal point.
        number_mantissa = str_num.split('e+')[0].split('.')[1]
        # For 1.23e+7, number = 123
        number = number_characteristic + number_mantissa
        zeros = ''
        # Eg: for this condition = 1.23e+7
        if e_power >= len(number_mantissa):
            # Number of zeros = e_power - mantissa length = 5
            for i in range(e_power - len(number_mantissa)):
                zeros = zeros + '0'
            # Scientific notation 1.23e+7 in regular decimal = 12300000.0
            str_num = number + zeros + '.0'
        # Eg: for this condition = 1.23e+1
        if e_power < len(number_mantissa):
            # In this case, we only need to shift the decimal e_power digits to the right
            # So we just copy the digits from mantissa to characteristic and then remove
            # them from mantissa.
            for i in range(e_power):
                number_characteristic = number_characteristic + number_mantissa[i]
            number_mantissa = number_mantissa[i:]
            # Scientific notation 1.23e+1 in regular decimal = 12.3
            str_num = number_characteristic + '.' + number_mantissa
    # characteristic is number left of decimal point.
    characteristic_part = str_num.split('.')[0]
    # mantissa is number right of decimal point.
    mantissa_part = str_num.split('.')[1]
    # If number is supposed to be rounded to whole number,
    # check first decimal digit. If more than 5, return
    # characteristic + 1 else return characteristic
    if precision == 0:
        if mantissa_part and int(mantissa_part[0]) >= 5:
            return type_num(int(characteristic_part) + 1)
        return type_num(characteristic_part)
    # Get the precision of the given number.
    num_precision = len(mantissa_part)
    # Rounding off is done only if number precision is
    # greater than requested precision
    if num_precision <= precision:
        return num
    # Replace the last '5' with 6 so that rounding off returns desired results
    if str_num[-1] == '5':
        str_num = re.sub('5$', '6', str_num)
    result = round(type_num(str_num), precision)
    # If the number was negative, add negative context back
    if negative_number:
        result = result * -1
    return result

@Dondon Jie 2018-03-08 03:29:12

Code:

x1 = 5.63
x2 = 5.65
print(float('%.2f' % round(x1,1)))  # gives you '5.6'
print(float('%.2f' % round(x2,1)))  # gives you '5.7'

Output:

5.6
5.7

@Gregory Pittman 2018-02-22 01:14:59

Here's where I see round failing. What if you wanted to round these 2 numbers to one decimal place? 23.45 23.55 My education was that from rounding these you should get: 23.4 23.6 the "rule" being that you should round up if the preceding number was odd, not round up if the preceding number were even. The round function in python simply truncates the 5.

@Simon MᶜKenzie 2018-02-22 01:40:19

What you're talking about is "bankers' rounding", one of many different ways to perform rounding.

@Gildas 2017-02-24 15:57:46

I am doing:

int(round( x , 0))

In this case, we first round properly at the unit level, then we convert to integer to avoid printing a float.

so

>>> int(round(5.59,0))
6

I think this answer works better than formating the string, and it also makes more sens to me to use the round function.

@Станислав Повышев 2015-11-18 04:10:22

Works Perfect

format(5.59, '.1f') # to display
float(format(5.59, '.1f')) #to round

@Alexandre Lymberopoulos 2013-06-25 12:00:58

It's a big problem indeed. Try out this code:

print "%.2f" % (round((2*4.4+3*5.6+3*4.4)/8,2),)

It displays 4.85. Then you do:

print "Media = %.1f" % (round((2*4.4+3*5.6+3*4.4)/8,1),)

and it shows 4.8. Do you calculations by hand the exact answer is 4.85, but if you try:

print "Media = %.20f" % (round((2*4.4+3*5.6+3*4.4)/8,20),)

you can see the truth: the float point is stored as the nearest finite sum of fractions whose denominators are powers of two.

@Robert Griesmeyer 2013-03-13 23:48:33

If you use the Decimal module you can approximate without the use of the 'round' function. Here is what I've been using for rounding especially when writing monetary applications:

Decimal(str(16.2)).quantize(Decimal('.01'), rounding=ROUND_UP)

This will return a Decimal Number which is 16.20.

@Cecil Curry 2016-02-06 05:00:28

This is the canonical answer – where accuracy matters, anyway, which is pretty much everywhere. Sure: it's a bit verbose. But throw that sucker in a helper function and you're good to format and go.

@LMc 2018-07-03 18:15:04

rounding='ROUND_UP'

@Stephen Blair 2018-07-18 15:20:47

If you get this error NameError: global name 'ROUND_UP' is not defined you need to import your rounding function: from decimal import Decimal, ROUND_UP. Other rounding functions

@ima 2008-09-11 15:11:04

What about:

round(n,1)+epsilon

@Michael Scott Cuthbert 2016-08-27 01:34:52

That would only work if the rounding were consistently off from the round number by epsilon. If epsilon = .000001 then round(1.0/5.0, 1) + epsilon would take the precise representation 0.2 and make it 0.00001. Equally bad problems would happen if the epsilon were inside the round function.

@Vinko Vrsalovic 2008-09-11 15:14:22

Formatting works correctly even without having to round:

"%.1f" % n

@whereswalden 2014-08-07 12:56:32

According to the docs, this style of string formatting will eventually go away. The new-style format would be "{:.1f}".format(n)

@schlamar 2015-07-14 09:48:01

Does not round correctly: '%.5f' % 0.988625 gives 0.98862

@Vinko Vrsalovic 2015-07-16 08:13:00

@schlamar: That's round()'s behavior as well: round(0.988625,5) also gives 0.98862. round(0.988626,5) as well as "%.5f" % 0.988626 give 0.98863

@Dion 2016-05-04 21:10:35

unfortunately "%.2f" % 2.675 will return 2.67 - which might be an unexpected answer for those using this method and expecting 2.68

@Jesse Dhillon 2010-07-12 04:12:38

Take a look at the Decimal module

Decimal “is based on a floating-point model which was designed with people in mind, and necessarily has a paramount guiding principle – computers must provide an arithmetic that works in the same way as the arithmetic that people learn at school.” – excerpt from the decimal arithmetic specification.

and

Decimal numbers can be represented exactly. In contrast, numbers like 1.1 and 2.2 do not have an exact representations in binary floating point. End users typically would not expect 1.1 + 2.2 to display as 3.3000000000000003 as it does with binary floating point.

Decimal provides the kind of operations that make it easy to write apps that require floating point operations and also need to present those results in a human readable format, e.g., accounting.

@Will Harris 2008-09-11 21:27:35

round(5.59, 1) is working fine. The problem is that 5.6 cannot be represented exactly in binary floating point.

>>> 5.6
5.5999999999999996
>>> 

As Vinko says, you can use string formatting to do rounding for display.

Python has a module for decimal arithmetic if you need that.

@vy32 2017-02-05 12:47:13

This is no l longer a problem with either Python 2.7 or Python 3.5

@Jason Navarrete 2008-09-11 15:14:15

printf the sucker.

print '%.1f' % 5.59  # returns 5.6

@spoulson 2008-09-11 15:13:32

Floating point math is vulnerable to slight, but annoying, precision inaccuracies. If you can work with integer or fixed point, you will be guaranteed precision.

@Tomi Kyöstilä 2008-09-11 15:13:28

You get '5.6' if you do str(round(n, 1)) instead of just round(n, 1).

@nlucaroni 2008-09-11 15:12:25

You can use the string format operator %, similar to sprintf.

mystring = "%.2f" % 5.5999

Related Questions

Sponsored Content

27 Answered Questions

29 Answered Questions

[SOLVED] Finding the index of an item given a list containing it in Python

  • 2008-10-07 01:39:38
  • Eugene M
  • 3153285 View
  • 2598 Score
  • 29 Answer
  • Tags:   python list

63 Answered Questions

[SOLVED] Round to at most 2 decimal places (only if necessary)

25 Answered Questions

[SOLVED] How can I safely create a nested directory in Python?

15 Answered Questions

[SOLVED] What are metaclasses in Python?

23 Answered Questions

[SOLVED] What is the difference between @staticmethod and @classmethod?

12 Answered Questions

17 Answered Questions

[SOLVED] Does Python have a string 'contains' substring method?

55 Answered Questions

[SOLVED] Calling an external command in Python

25 Answered Questions

[SOLVED] How do I parse a string to a float or int in Python?

Sponsored Content