将数字四舍五入到最近的整数

332

我一直在尝试对长浮点数进行四舍五入,例如:

32.268907563;
32.268907563;
31.2396694215;
33.6206896552;
...

迄今为止没有成功。我尝试过 math.ceil(x)math.floor(x)(虽然这会四舍五入,但不是我要找的)以及 round(x),但都没有起作用(仍然是浮点数)。

我该怎么办?

代码:

for i in widthRange:
    for j in heightRange:
        r, g, b = rgb_im.getpixel((i, j))
        h, s, v = colorsys.rgb_to_hsv(r/255.0, g/255.0, b/255.0)
        h = h * 360
        int(round(h))
        print(h)

5
我会尝试使用 int(x) - The Brofessor
这不会导致错误吗?int()函数的参数无效,应该是十进制数。 - snh_nl
16个回答

537

TL;DR:

round(x)

将其四舍五入并转换为整数。

您没有给任何变量分配round(h)。当您调用round(h)时,它会返回整数,但不进行其他操作;您必须更改该行的内容:

h = round(h)

为给h赋新值,可以采用以下方法。


正如@plowman在评论中所说,Python的round()函数不像通常期望的那样工作,这是因为变量存储的数字通常不是屏幕上看到的数字。有许多答案解释了这种行为。

避免此问题的一种方法是使用此回答中提到的Decimal。

为了使该答案能够正常工作而不使用额外的库,最好使用自定义的舍入函数。我想出了以下解决方案,根据我的测试,它避免了所有存储问题。它基于使用字符串表示形式,使用repr()获取(而非str()!)。它看起来很奇怪,但这是我发现解决所有情况的唯一方法。它适用于Python2和Python3。

def proper_round(num, dec=0):
    num = str(num)[:str(num).index('.')+dec+2]
    if num[-1]>='5':
        return float(num[:-2-(not dec)]+str(int(num[-2-(not dec)])+1))
    return float(num[:-1])

测试:

>>> print(proper_round(1.0005,3))
1.001
>>> print(proper_round(2.0005,3))
2.001
>>> print(proper_round(3.0005,3))
3.001
>>> print(proper_round(4.0005,3))
4.001
>>> print(proper_round(5.0005,3))
5.001
>>> print(proper_round(1.005,2))
1.01
>>> print(proper_round(2.005,2))
2.01
>>> print(proper_round(3.005,2))
3.01
>>> print(proper_round(4.005,2))
4.01
>>> print(proper_round(5.005,2))
5.01
>>> print(proper_round(1.05,1))
1.1
>>> print(proper_round(2.05,1))
2.1
>>> print(proper_round(3.05,1))
3.1
>>> print(proper_round(4.05,1))
4.1
>>> print(proper_round(5.05,1))
5.1
>>> print(proper_round(1.5))
2.0
>>> print(proper_round(2.5))
3.0
>>> print(proper_round(3.5))
4.0
>>> print(proper_round(4.5))
5.0
>>> print(proper_round(5.5))
6.0
>>> 
>>> print(proper_round(1.000499999999,3))
1.0
>>> print(proper_round(2.000499999999,3))
2.0
>>> print(proper_round(3.000499999999,3))
3.0
>>> print(proper_round(4.000499999999,3))
4.0
>>> print(proper_round(5.000499999999,3))
5.0
>>> print(proper_round(1.00499999999,2))
1.0
>>> print(proper_round(2.00499999999,2))
2.0
>>> print(proper_round(3.00499999999,2))
3.0
>>> print(proper_round(4.00499999999,2))
4.0
>>> print(proper_round(5.00499999999,2))
5.0
>>> print(proper_round(1.0499999999,1))
1.0
>>> print(proper_round(2.0499999999,1))
2.0
>>> print(proper_round(3.0499999999,1))
3.0
>>> print(proper_round(4.0499999999,1))
4.0
>>> print(proper_round(5.0499999999,1))
5.0
>>> print(proper_round(1.499999999))
1.0
>>> print(proper_round(2.499999999))
2.0
>>> print(proper_round(3.499999999))
3.0
>>> print(proper_round(4.499999999))
4.0
>>> print(proper_round(5.499999999))
5.0

最终,经过更正的答案将是:

# Having proper_round defined as previously stated
h = int(proper_round(h))

测试:

>>> proper_round(6.39764125, 2)
6.31 # should be 6.4
>>> proper_round(6.9764125, 1)
6.1  # should be 7

需要注意的是,第dec个小数位可能为9,如果第dec+1个数字>=5,则9将变为0,并且应该向dec-1个数字进位1。

如果我们考虑到这一点,我们将得到:

def proper_round(num, dec=0):
    num = str(num)[:str(num).index('.')+dec+2]
    if num[-1]>='5':
      a = num[:-2-(not dec)]       # integer part
      b = int(num[-2-(not dec)])+1 # decimal part
      return float(a)+b**(-dec+1) if a and b == 10 else float(a+str(b))
    return float(num[:-1])
在上述情况中,b = 10,之前的版本只会将ab连接起来,导致10被拼接,末尾的0会消失。这个版本基于decb转换为正确的十进制位数,作为适当的进位。

4
print("4.5)", int(round(4.5))) # 输出了 4 print("5.5)", int(round(5.5))) # 输出了 6 :,( - Komm
1
值得注意的是:这个解决方案并不像你预期的那样四舍五入。例如,int(round(4.5)) 向下舍入为 4,而 int(round(4.500001)) 正确地舍入为 5。 - plowman
5
如果你想要一个整数,那么在Python 3.6.2(以及可能更低版本)中使用round(x)就足够了。结果已经是整数类型。注意:round(x, n)将会是浮点数类型。 - Elmex80s
1
这对于112439.50093565206不起作用。它的输出是-> 11253.0。该死的奇怪..!!!! - ajin
2
proper_round(19.5)对于自定义函数的两个版本都给出了非常错误的答案。对于第一个版本,它返回110.0,而对于第二个版本,它返回11.0。 - iagerogiannis
显示剩余5条评论

29

使用round(x, y)函数,它将把你的数字四舍五入到指定的小数位。

例如:

>>> round(32.268907563, 3)
32.269

6
在较新版本的Python(>=3.6)中,使用round(x)将已经返回一个整数。 - Stack

25
round(value,significantDigit)是普遍的解决方案,但是当需要四舍五入的数字刚好以5结尾时,从数学角度来看该方法并不会按照预期操作。如果5在被舍入到的那个数字的下一位,这些值只有在某些情况下才会按照预期进行四舍五入(例如将8.005舍入到两位小数得到8.01)。由于浮点数运算的特殊性质,对于某些值,它们实际上会被舍入到比预期更小的值!

例如:

>>> round(1.0005,3)
1.0
>>> round(2.0005,3)
2.001
>>> round(3.0005,3)
3.001
>>> round(4.0005,3)
4.0
>>> round(1.005,2)
1.0
>>> round(5.005,2)
5.0
>>> round(6.005,2)
6.0
>>> round(7.005,2)
7.0
>>> round(3.005,2)
3.0
>>> round(8.005,2)
8.01

奇怪。

假设你的意图是在科学统计中进行传统四舍五入,那么这是一个方便的包装器,可以使 round 函数按预期工作,而不需要导入额外的东西,如 Decimal

>>> round(0.075,2)

0.07

>>> round(0.075+10**(-2*5),2)

0.08

啊哈!所以基于这个,我们可以制作一个函数...

def roundTraditional(val,digits):
   return round(val+10**(-len(str(val))-1), digits)
基本上,这会添加一个值保证比您尝试使用round的字符串的最小给定数字更小。通过添加这个小量,它在大多数情况下保留了round的行为,同时现在确保如果比要舍入到的数字低的位是5,则向上舍入,如果是4,则向下舍入。
使用10**(-len(val)-1)的方法是故意的,因为它是您可以添加以强制进行移位的最大小数,并且还确保您添加的值即使没有小数.也永远不会改变舍入。我可以只使用10**(-len(val))并带有条件性的if (val>1)来减去1个更多...但是总体来说直接减去1会更简单,因为这不会太大程度改变此解决方法可正确处理的十进制数字范围。 如果您的值达到类型的限制,则此方法将失败,但对于几乎整个有效十进制值范围,它应该正常工作。
您还可以使用 decimal 库来实现此目的,但是我提出的包装器更简单,并且在某些情况下可能更受欢迎。
编辑:感谢 Blckknght 指出5边缘情况仅适用于某些值。 此外,此答案的早期版本没有明确说明奇怪的舍入行为仅在比您要舍入到的数字低的位数中的数字为5时发生。

有趣...你说得对。round(4.0005,3)会得到4.0,而round(1.0005,3)会得到1.0,但是round(2.0005,3)会得到2.001,而round(3.0005,3)会得到3.001。但这正是为什么我的解决方案是必要的...你不知道从标准的round函数中会得到什么结果,在这种重要的情况下! - Jason R. Mick
谢谢你。当这个问题出现时,你的函数会派上用场。 - TMWP
1
你是不是想在那个返回语句的末尾加上 , digits?别误会,我没有恶意。(我的意思是“mean”,而不是“恶意”) - user3342816
round(2345, -2) --> 2300.0! - Saish
这是预期的... round(x, 0):四舍五入到个位数; round(x, -1):四舍五入到十位数; round(x, -2):四舍五入到百位数。在您的示例中,普通的 round 函数产生可预测的结果。从我的粗略测试中,我没有看到普通的 round 在值 >= 1 中产生不规则的舍入,尽管它可能会在某些值或大值上出现。该方法的前提是添加一个非常小的数字(输入字符串长度减一的否定是有点任意选择),以确保在将第二个 LSD 舍入时,小数 0.#....#50 的情况表现得可预测。 - Jason R. Mick
显示剩余3条评论

23

对于积极的事情,尝试

int(x + 0.5)

为了使它也适用于负数,请尝试

int(x + (0.5 if x > 0 else -0.5))

int() 的作用类似于 floor 函数,因此您可以利用这个特性。这绝对是最快的方法。


4
不适用于负数。 >>> x=-0.999 >>> int(x), round(x), int(x+0.5) (0, -1.0, 0) - user2907934
3
如果您关心边角情况,请不要使用“加0.5再向下取整”的技巧 - 有些值可能无法按您的期望舍入!请参见 https://dev59.com/5FYN5IYBdhLWcg3whYe3#47302585 获取C++的解决方案,以及这个问题中的 https://dev59.com/A1wZ5IYBdhLWcg3wINTQ#38744026 答案。 - Anon
我需要一种快速的方法,它不必十分准确,也没有太多的特殊情况,而且在我的场景中角落情况的错误并不重要。因此,当速度优先时,这绝对是我的首选之一。不建议用于精度或准确性。 - AgentM

17

您的解决方案在调用round时没有指定第二个参数(小数位数)

>>> round(0.44)
0
>>> round(0.64)
1

这比原来的结果好得多。

>>> int(round(0.44, 2))
0
>>> int(round(0.64, 2))
0

来自Python文档,网址为https://docs.python.org/3/library/functions.html#round

round(number[, ndigits])

返回小数点后保留ndigits位精度的number四舍五入后的值。如果省略ndigits或其为None,则返回其输入的最接近整数。

注意

对浮点数进行round()操作可能会产生意外的结果:例如,round(2.675, 2)得到的是2.67而不是预期的2.68。这不是错误,而是因为大多数十进制小数不能完全表示为浮点数的结果。有关更多信息,请参见“浮点数算术:问题和限制”。


12

2
这个答案有点不明确。 Round half to even 绝对不是 IEEE 754 规定的,而只是标准描述的几种舍入选项之一。Round to nearest, ties away from zero(即大多数人期望的行为)也是一种选择,并且是例如 C / C++ 中的默认选项。 - tel
我同意,措辞相当令人困惑。我的意思是Python将四舍五入到最近的偶数(请参见https://docs.python.org/3.7/library/stdtypes.html#numeric-types-int-float-complex末尾的表格,其中解释了“round”),并且它是根据标准规定的“四舍五入到最近的偶数”的方式进行操作的。 - user109839

11

如果您正在使用Python3.x,您还可以使用NumPy。以下是一个示例:

import numpy as np
x = 2.3
print(np.rint(x))
>>> 2.0

1
在正确的答案中提到,如果我使用79.5,它就不起作用。它会给出710的答案,而不是80。
def proper_round(num, dec=0):
num= int(num*pow(10,dec+1))
lastDigit = (str(num))[-1]
num = int(num/10)
if lastDigit >='5':
    num+=1
return float(num/pow(10,dec))

上述代码可以获得更好的结果。

1

将x500...四舍五入为偶数值是正确的方法,与该值的二进制表示无关。这是应该采用的“适当”算法。高级科学课程和统计课程教授这一点。但为什么会这样呢?

最小化累积误差!

当您四舍五入一个值时,会在该项上引入一个误差值。如果您正在对大量值进行求和,并且始终将x500...值向上舍入[即到x+1],那么您将具有误差累积的固有偏差。

在N个数字的集合中,其中M个数字的形式为x500...,向上舍入的方法将引入一个+ve误差项,其大小为M * 0.5舍入到的数字。

然而,如果您总是舍入为偶数,则可以期望这些M个误差项的一半为+ve,一半为-ve。因此,您期望它们将取消,使您从这些M个项中获得净零误差。零误差不比M * 0.5的误差好吗?

然而,那个净0误差只是一个期望。当你考虑抛一枚“公平”的硬币(类似的问题),使得“正面”= +1,“反面”= -1,M次,并将所有抛掷的总和相加时,有一个统计期望为0的结果,但是从0开始的“漫步”的大小(可能是+ve或-ve)通常受到sqrt(M)的限制——或者是log(M)?我已经很长时间没有打开过统计书了。实际答案(sqrt或log)与这个问题无关,因为对于所有M>1,sqrt(M)和log(M)都小于M。

因此,就我们对数字求和的情况而言,由于M值形式为x500...,因为M是有限的,我们期望这些M值的误差项为零,但我们还有一个更强的期望,即误差项的大小应该受到sqrt(M)* 0.5舍入到的数字的影响。

0虽然很棒,但是一个预期的sqrt(M) * 0.5仍然比保证的M * 0.5好得多。

是的,你可能会遇到一些病态情况,导致M * 0.5,但这种情况应该是异常而不是正常情况——而且那个罕见的异常点也不比“总是向上取整”的方法更糟糕。


1
如果您需要对A进行两位数的近似值,那么int(A*100+0.5)/100.0将会达到您所需要的效果。
如果您需要三位数的近似值,则可以乘以和除以1000等操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接