round()似乎无法正确舍入

问题:round()似乎无法正确舍入

round()函数的文档指出,您向其传递了一个数字,并将小数点后的位置传递给四舍五入。因此,它应该这样做:

n = 5.59
round(n, 1) # 5.6

但是,实际上,老的浮点怪异现象不断蔓延,您会得到:

5.5999999999999996

出于UI的目的,我需要显示5.6。我在Internet上闲逛,发现一些文档取决于我对Python的实现。不幸的是,这在我的Windows开发机和我尝试过的每台Linux服务器上都会发生。另请参阅此处

除了创建自己的回合库之外,还有什么办法可以解决?

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?


回答 0

我不知道它的存储方式,但至少格式化正确:

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

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

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

回答 1

格式化无需四舍五入即可正确进行:

"%.1f" % n

Formatting works correctly even without having to round:

"%.1f" % n

回答 2

如果使用小数模块,则无需使用“舍入”功能就可以近似。这是我用于舍入的内容,尤其是在编写货币应用程序时:

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

这将返回一个十进制数为16.20。

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.


回答 3

round(5.59, 1)工作正常。问题在于5.6无法精确地用二进制浮点表示。

>>> 5.6
5.5999999999999996
>>> 

正如Vinko所说,您可以使用字符串格式对显示进行四舍五入。

如果需要,Python有一个用于十进制算术模块

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.


回答 4

如果您执行此操作,str(round(n, 1))而不是,则会得到“ 5.6” round(n, 1)

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


回答 5

您可以将数据类型切换为整数:

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

然后通过插入语言环境的小数点分隔符来显示数字。

但是,吉米的答案更好。

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.


回答 6

浮点数学容易受到轻微但令人讨厌的精度误差的影响。如果可以使用整数或定点,则可以保证精度。

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.


回答 7

看一下Decimal模块

十进制“基于浮点模型,该浮点模型是为人而设计的,并且必然具有最重要的指导原则–计算机必须提供一种与人们在学校学习的算法相同的算法。” –摘自十进制算术规范。

小数可以精确表示。相反,像1.1和2.2这样的数字在二进制浮点数中没有确切的表示形式。最终用户通常不会期望1.1 + 2.2像二进制浮点那样显示为3.3000000000000003。

Decimal提供了一种操作,使编写需要浮点运算的应用程序变得容易,并且需要以人类可读的格式(例如记帐)显示这些结果。

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.


回答 8

打印吸盘。

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

printf the sucker.

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

回答 9

确实是个大问题。试用以下代码:

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

显示4.85。然后,您执行以下操作:

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

它显示4.8。您手动计算的确切答案是4.85,但是如果尝试:

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

您会看到事实:浮点存储为分母为2的幂的分数的最接近有限和。

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.


回答 10

您可以使用%类似于sprintf 的字符串格式运算符。

mystring = "%.2f" % 5.5999

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

mystring = "%.2f" % 5.5999

回答 11

完美的作品

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

Works Perfect

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

回答 12

我在做:

int(round( x , 0))

在这种情况下,我们首先在单位级别正确舍入,然后转换为整数以避免打印浮点数。

所以

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

我认为这个答案比格式化字符串更好,并且使用round函数对我也更有意义。

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.


回答 13

round()在这种情况下,我将完全避免依赖。考虑

print(round(61.295, 2))
print(round(1.295, 2))

将输出

61.3
1.29

如果您需要四舍五入到最接近的整数,则这不是理想的输出。要绕过此行为,请使用math.ceil()(或math.floor()如果要舍入):

from math import ceil
decimal_count = 2
print(ceil(61.295 * 10 ** decimal_count) / 10 ** decimal_count)
print(ceil(1.295 * 10 ** decimal_count) / 10 ** decimal_count)

输出

61.3
1.3

希望有帮助。

I would avoid relying on round() at all in this case. Consider

print(round(61.295, 2))
print(round(1.295, 2))

will output

61.3
1.29

which is not a desired output if you need solid rounding to the nearest integer. To bypass this behavior go with math.ceil() (or math.floor() if you want to round down):

from math import ceil
decimal_count = 2
print(ceil(61.295 * 10 ** decimal_count) / 10 ** decimal_count)
print(ceil(1.295 * 10 ** decimal_count) / 10 ** decimal_count)

outputs

61.3
1.3

Hope that helps.


回答 14

码:

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'

输出:

5.6
5.7

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

回答 15

这是我看到回合失败的地方。如果您想将这两个数字四舍五入到小数点后该怎么办?23.45 23.55我的教育是,通过对这些数字进行四舍五入,您将获得:23.4 23.6“规则”是,如果前一个数字为奇数,则应四舍五入,如果前一个数字为偶数,则不四舍五入。python中的round函数将截断5。

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.


回答 16

问题仅在最后一位数字为5时出现。0.045在内部存储为0.044999999999999 …您可以将最后一位数字简单地增加到6并四舍五入。这将为您提供所需的结果。

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

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

回答 17

另一个可能的选择是:

def hard_round(number, decimal_places=0):
    """
    Function:
    - Rounds a float value to a specified number of decimal places
    - Fixes issues with floating point binary approximation rounding in python
    Requires:
    - `number`:
        - Type: int|float
        - What: The number to round
    Optional:
    - `decimal_places`:
        - Type: int 
        - What: The number of decimal places to round to
        - Default: 0
    Example:
    ```
    hard_round(5.6,1)
    ```
    """
    return int(number*(10**decimal_places)+0.5)/(10**decimal_places)

Another potential option is:

def hard_round(number, decimal_places=0):
    """
    Function:
    - Rounds a float value to a specified number of decimal places
    - Fixes issues with floating point binary approximation rounding in python
    Requires:
    - `number`:
        - Type: int|float
        - What: The number to round
    Optional:
    - `decimal_places`:
        - Type: int 
        - What: The number of decimal places to round to
        - Default: 0
    Example:
    ```
    hard_round(5.6,1)
    ```
    """
    return int(number*(10**decimal_places)+0.5)/(10**decimal_places)

回答 18

关于什么:

round(n,1)+epsilon

What about:

round(n,1)+epsilon