round() 似乎没有正确舍入

round() doesn't seem to be rounding properly

提问人:swilliams 提问时间:9/11/2008 最后编辑:martineauswilliams 更新时间:7/9/2023 访问量:279269

问:

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

n = 5.59
round(n, 1) # 5.6

但是,实际上,老式的浮点怪异悄悄地蔓延开来,你会得到:

5.5999999999999996

出于UI的目的,我需要显示.我在互联网上浏览了一下,发现一些文档表明这取决于我的 Python 实现。不幸的是,这发生在我的 Windows 开发机器和我尝试过的每台 Linux 服务器上。另请参阅此处5.6

除了创建我自己的圆形库之外,有什么办法可以解决这个问题吗?

Python 浮点 舍入

评论

4赞 Alex Punnen 4/11/2016
我用 python 2.7.11 round(5.59) 尝试过这个,它在 windows 和 linux x86 64 位机器中的结果为 5.6,Cython ?(我猜提到的文档链接现在已更改)
7赞 Dmitry 10/20/2017
它实际上无法正常工作的地方是 .round(5.55, 1) = 5.5
1赞 Irfan wani 1/19/2021
这回答了你的问题吗?将浮点数限制为小数点后两位

答:

-4赞 ima 9/11/2008 #1

怎么样:

round(n,1)+epsilon

评论

0赞 Michael Scott Asato Cuthbert 8/27/2016
只有当四舍五入始终与 epsilon 的整数相差时,这才有效。如果那样,将采用精确表示 0.2 并使其成为 0.00001。如果 epsilon 位于 round 函数内部,也会发生同样糟糕的问题。epsilon = .000001round(1.0/5.0, 1) + epsilon
110赞 Jimmy 9/11/2008 #2

我无法帮助它的存储方式,但至少格式化可以正常工作:

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

评论

16赞 Liza 7/15/2015
我试过了,但它回来了,应该是print '%.2f' % 655.665655.66655.67
1赞 Jimmy 7/16/2015
@Kyrie看到 stackoverflow.com/questions/9301690/...。浮点不准确是这里的罪魁祸首——“5.665 -> 5.67”,但“15.665 -> 15.66”。如果需要精确的精度,请使用小数。
8赞 Liza 7/16/2015
这在搜索后有效 :) # 用于四舍五入浮点数 # 浮点数的问题和限制from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWNDecimal(str(655.665)).quantize(Decimal('1.11'), rounding=ROUND_HALF_UP)
12赞 Frank Krueger 9/11/2008 #3

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

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

然后通过插入区域设置的小数点分隔符来显示数字。

然而,吉米的答案更好。

3赞 nlucaroni 9/11/2008 #4

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

mystring = "%.2f" % 5.5999
18赞 Tomi Kyöstilä 9/11/2008 #5

如果你这样做,你会得到“5.6”,而不仅仅是.str(round(n, 1))round(n, 1)

5赞 spoulson 9/11/2008 #6

浮点数学容易受到轻微但令人讨厌的精度不准确的影响。如果您可以使用整数或定点,您将获得精度保证。

4赞 Jason Navarrete 9/11/2008 #7

printf 傻瓜。

print '%.1f' % 5.59  # returns 5.6
104赞 Vinko Vrsalovic 9/11/2008 #8

即使无需舍入,格式化也能正常工作:

"%.1f" % n

评论

18赞 whereswalden 8/7/2014
根据文档,这种字符串格式最终会消失。新式格式将是"{:.1f}".format(n)
3赞 schlamar 7/14/2015
未正确舍入:给出'%.5f' % 0.9886250.98862
0赞 Vinko Vrsalovic 7/16/2015
@schlamar:这也是 round() 的行为:round(0.988625,5) 也给出了 0.98862。round(0.988626,5) 以及 “%.5f” % 0.988626 给出 0.98863
0赞 Dion 5/5/2016
不幸的是,“%.2f” % 2.675 将返回 2.67 - 对于那些使用此方法并期望 2.68 的人来说,这可能是一个意想不到的答案
23赞 Will Harris 9/12/2008 #9

round(5.59, 1)工作正常。问题是 5.6 不能用二进制浮点数精确表示。

>>> 5.6
5.5999999999999996
>>> 

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

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

评论

2赞 vy32 2/5/2017
这不再是 Python 2.7 或 Python 3.5 的问题
6赞 Jesse Dhillon 7/12/2010 #10

看一下 Decimal 模块

Decimal “基于浮点数 与人一起设计的模型 在脑海中,并且必然有一个 最重要的指导原则—— 计算机必须提供算术 其工作方式与 人们学习的算术 学校“——摘自小数点 算术规范。

可以表示十进制数 完全。相比之下,像 1.1 这样的数字 和 2.2 没有确切的 二进制浮点数中的表示 点。最终用户通常不会 预计 1.1 + 2.2 显示为 3.30000000000000003,就像二进制浮点一样。

Decimal 提供的操作类型使编写需要浮点运算的应用程序变得容易,并且还需要以人类可读的格式(例如会计)呈现这些结果。

38赞 Robert Griesmeyer 3/14/2013 #11

如果您使用 Decimal 模块,则可以在不使用“round”函数的情况下进行近似。以下是我一直在用于四舍五入的方法,尤其是在编写货币应用程序时:

from decimal import Decimal, ROUND_UP

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

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

评论

6赞 Cecil Curry 2/6/2016
这是典型的答案——无论如何,准确性很重要,这几乎无处不在。当然:这有点啰嗦。但是把这个傻瓜扔进一个辅助函数中,你就可以格式化了。
1赞 Stephen Blair 7/18/2018
如果出现此错误,则需要导入舍入函数:。其他舍入函数NameError: global name 'ROUND_UP' is not definedfrom decimal import Decimal, ROUND_UP
2赞 YvesgereY 10/2/2019
你的例子看起来仍然很危险:你依赖于 str() 提供的舍入。
5赞 Alexandre Lymberopoulos 6/25/2013 #12

这确实是一个大问题。试试这段代码:

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 的幂。

2赞 Станислав Повышев 11/18/2015 #13

完美作品

format(5.59, '.1f') # to display
float(format(5.59, '.1f')) #to round
3赞 Gildas 2/24/2017 #14

我正在做:

int(round( x , 0))

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

所以

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

我认为这个答案比格式化字符串效果更好,而且它也让我对使用 round 函数更加敏感。

0赞 Gregory Pittman 2/22/2018 #15

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

评论

2赞 Simon MᶜKenzie 2/22/2018
你说的是“银行家的四舍五入”,这是执行四舍五入的众多不同方法之一。
1赞 Dondon Jie 3/8/2018 #16

法典:

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
1赞 Syed Is Saqlain 4/11/2018 #17

只有当最后一位数字为 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
4赞 Tali Oat 2/19/2020 #18

在这种情况下,我会完全避免依赖。考虑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

希望能有所帮助。

2赞 conmak 5/15/2020 #19

另一个可能的选择是:

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)
-1赞 Irfan wani 1/19/2021 #20

这是一种将浮点数四舍五入到任意小数位数的简单方法,它在 2021 年仍然有效!

float_number = 12.234325335563
rounded = round(float_number, 3) # 3 is the number of decimal places to be returned.You can pass any number in place of 3 depending on how many decimal places you want to return.
print(rounded)

这将打印;

12.234
0赞 Mohammed Alali 4/7/2023 #21

我在使用 round() 时遇到了同样的错误。现在,我像这样使用 ceil() 或 floor():

import math
x = 76.5
x_str = str(x)
dot_index = x_str.index('.')
if(int(x_str[dot_index+1:]) >= 5):
  x = math.ceil(x)
else:
  x = math.floor(x)

评论

0赞 Lutz Lehmann 4/7/2023
这仅给出一个非重复字符串,这是不够的,例如,如果您想四舍五入到 10 位数字。用于获取具有代表性的字符串。你有没有测试过在有问题的情况下是否真的得到了不同的结果?x_str = f"{x:.20f}"