【问题标题】:Round with integer division整数除法四舍五入
【发布时间】:2010-10-16 19:15:39
【问题描述】:

是否有一种简单的 Pythonic 方法可以在不使用浮点的情况下舍入到最接近的整数?我想使用整数算术执行以下操作:

skip = int(round(1.0 * total / surplus))

==============

@John:浮点无法跨平台重现。如果您希望您的代码跨不同平台通过测试,那么您需要避免使用浮点(或在您的测试中添加一些 hacky espilon 的东西并希望它有效)。以上可能很简单,在大多数/所有平台上都是相同的,但我宁愿不做出这样的决定,因为完全避免浮点更容易。怎么会“不符合 Python 的精神”?

【问题讨论】:

  • @John:嗯,Python 中的 long 可以存储任意大的值,其中浮点数是固定精度的,因此在将浮点数引入整数运算时存在范围、复杂性和可能的​​错误。不过,我确实希望人们不要再用愚蠢的流行词“Pythonic”来回答每个问题。
  • @GlennMaynard 真的!这不是很 Pythical。

标签: python rounding integer-division


【解决方案1】:

你可以很简单地做到这一点:

(n + d // 2) // d,其中n 是被除数,d 是除数。

在最近的 CPython 中,(((n << 1) // d) + 1) >> 1 或等效的 (((n * 2) // d) + 1) // 2 之类的替代方案可能会更慢,其中 int 的实现方式与旧的 long 类似。

简单的方法进行 3 次变量访问、1 次常量加载和 3 次整数运算。复杂的方法进行 2 次变量访问、3 次常量加载和 4 次整数运算。整数运算可能需要时间,这取决于所涉及数字的大小。函数局部变量的变量访问不涉及“查找”。

如果您真的对速度感到绝望,请进行基准测试。否则,KISS。

【讨论】:

  • +1 这种方法比位移方法更具可读性,而且在 Py 2.7 上也更快(在 timeit 测试中)。
【解决方案2】:
skip = (((total << 1) // surplus) + 1) >> 1

将事物左移一位有效地乘以二,将事物右移一位有效地除以二四舍五入。如果结果超过 0.5 位小数部分,则在中间添加一个使得“向下舍入”实际上是向上舍入。

基本上和你写的一样……

skip = int((1.0*total/surplus) + 0.5)

除了所有的东西都乘以 2,然后再除以 2,这是你可以用整数算术做的事情(因为位移不需要浮点数)。

【讨论】:

  • 正确的想法,但我认为您需要添加到total 的数量与surplus 相当。我会在您当前的公式中将“+ 1”替换为“+ 盈余”,这可能是正确的。
  • 其实我只要把1移到外面就行了。 :) 相当于在里面加了余数,但是需要的查找更少。
  • 谢谢!对我来说,这在所有边缘情况下都能正常工作并不是很明显,我会确认一下。
  • 我建议将乘以 2 再乘以 2,除以 2 再除以 2,除非这实际上是经过分析的性能敏感代码。
【解决方案3】:

灵感来自zhmyh's answer 答案,这是

q, r = divmod(total, surplus)
skip = q + int(bool(r)) # rounds to next greater integer (always ceiling)

,我想出了以下解决方案:

q, r = divmod(total, surplus) 
skip = q + int(2 * r >= surplus) # rounds to nearest integer (floor or ceiling)

由于 OP 要求四舍五入到 最近的整数,所以 zhmhs 的解决方案实际上有点不正确,因为它总是四舍五入到 下一个更大的整数,而我的解决方案按要求工作。

(如果您觉得我的回答最好是对 zhmh 的回答进行编辑或评论,让我指出我建议的编辑被拒绝了,因为它最好是评论,但我愿意还没有足够的声誉来发表评论!)

如果您想知道divmod 是如何定义的:根据它的documentation

对于整数,结果与(a // b, a % b) 相同。

因此,按照 OP 的要求,我们坚持使用整数运算。

【讨论】:

    【解决方案4】:

    另一种有趣的方式:

    q, r = divmod(total, surplus)
    skip = q + int(bool(r))
    

    【讨论】:

    • 请注意,此解决方案会舍入到下一个更大的整数,该整数不一定是最接近的整数。请参阅我对固定版本的回答(我在没有足够的评论声誉时发布。).
    【解决方案5】:

    在除法之前只需注意舍入规则。对于最简单的四舍五入:

    if total % surplus < surplus / 2:
        return total / surplus
    else:
        return (total / surplus) + 1
    

    如果您需要进行适当的四舍五入,请稍作调整。

    【讨论】:

    • 模数和除法运算符非常昂贵,此代码运行 3 个除法运算(1 个模数和 2 个正则除法),因此如果代码需要快速,这不是最佳选择。
    【解决方案6】:

    这应该也可以:

    def rint(n):
        return (int(n+.5) if n > 0 else int(n-.5))
    

    【讨论】:

    • 这不是问题的答案,因为它涉及n + .5 中的浮点运算。 @rubik我没有投票,那是其他人。 ;-)
    • @ArneL.: 好吧,没关系,如果错了,投反对票是对的 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多