如何在Python中实现round-towards-infinity的划分

时间:2011-08-24 20:20:14

标签: python math floating-point rounding division

我希望3/2等于2而不是1.5

我知道这个操作有一个数学术语(不称为四舍五入),但我现在不记得了。 无论如何,如何在不做两个功能的情况下做到这一点?

前面我不想要的东西:

answer = 3/2 then math.ceil(answer)=2 (why does math.ceil(3/2)=1?)  

前面我想要的:

 "function"(3/2) = 2

8 个答案:

答案 0 :(得分:16)

简短回答......

Python仅提供两种类型的本地运算符:“true”除法和“舍入”除法。所以你想要的不是单一功能。然而,可以使用一些短表达式容易地实现许多不同类型的舍入除法。

根据标题的请求:给定严格的整数输入,可以使用(a+(-a%b))//b实现“向上舍入”除法,并且可以使用更复杂的a//b if a*b<0 else (a+(-a%b))//b来实现“从零开始”除法。其中一个可能就是你想要的。至于为什么......


要给出更长的答案......

首先,让我回答有关3/2==1math.ceil(3/2)==1.0原因的问题,以解释Python除法运算符的工作原理。有两个主要问题在起作用......

float vs int除法:在Python 2下,除法根据输入的类型而有所不同。如果ab都是整数,a/b执行“向下舍入”或“整数整数”除法(例如3/2==1,但-3/2==-2)。这相当于int(math.floor(float(a)/b))

但如果ab中至少有一个是浮点数,则Python执行“真正”除法,并为您提供float结果(例如3.0/2==1.5和{ {1}})。这就是为什么你有时会看到构造-3.0/2==-1.5:它被用来强制真正的除法,即使两个输入都是整数(例如float(a)/b)。这就是您的示例float(3)/2==1.5返回math.ceil(3/2),而1.0返回math.ceil(float(3)/2)的原因。结果在达到2.0之前已经向下舍入。

默认情况下

“真正的划分”:在2001年,决定(PEP 238)应该更改Python的除法运算符,以便它始终执行“真正”除法,无论是否输入是浮点数或整数(例如,这将使math.ceil())。为了不破坏现有脚本,默认行为的更改推迟到Python 3.0;为了在Python 2.x下获得此行为,您必须通过将3/2==1.5添加到文件顶部来为每个文件启用它。否则,将使用旧的类型依赖行为。

但是仍然经常需要“向下”分组,所以PEP并没有完全采用它。相反,它引入了一个新的除法运算符:from __future__ import division始终执行向下舍入,即使输入包含浮点数。这可以在Python 2.2+和3.x下使用而不做任何特殊操作。


出于这种方式,分为四舍五入:

为了简化操作,以下表达式在处理整数时都使用a//b运算符,因为它在所有python版本下的行为都相同。同样,我假设a//b 0<=a%b<b为正,b如果b为负。这就是Python的行为方式,但其他语言的模数运算符可能略有不同。

带舍入的四种基本整数除法类型:

  • “round down”又名“floor integer”又名“round to minus infinity”divsion:python通过b<=a%b<=0原生提供此功能。

  • “round up”又名“天花板整数”又称“圆形到正无限”分区:这可以通过a//bint(math.ceil(float(a)/b))来实现。后一个等式有效,因为如果(a+(-a%b))//b-a%b的倍数,则a为0,否则我们需要添加到b以达到下一个最高倍数

  • “向零舍去”又称“截断”除法 - 这可以通过a来实现。在不使用浮点的情况下这样做是比较棘手的...因为Python只提供向下舍入整数除法,而int(float(a)/b)运算符具有类似的向下舍入偏差,所以我们没有任何非浮点运算符对称的圆形约为0.所以我能想到的唯一方法就是用向下舍入和向上舍入来构造一个分段表达式:%

  • “远离零”又称“圆到(无)无限”分区 - 不幸的是,这甚至比圆向零更棘手。我们不能再利用a//b if a*b>0 else (a+(-a%b))//b运算符的截断行为,因此即使包含浮点运算,我也无法想到一个简单的表达式。所以我必须使用从零到零的表达式的倒数,并使用int

请注意,如果您只使用正整数,a//b if a*b<0 else (a+(-a%b))//b提供的舍入/偏离零比任何上述解决方案更有效,但对于负数则会分崩离析。

希望有帮助...并且很高兴能够编辑,如果有人可以建议更好的方程式来回零/远离零。我发现那些我特别不满意的。

答案 1 :(得分:5)

Python 3中的积分除法:

3 // 2 == 1

Python 3中的非整数除法:

3 / 2 == 1.5

你所谈论的并不是一个分工。

答案 2 :(得分:4)

OP的问题的意图是“如何在Python中实现圆向无限的划分”(建议您更改标题)。

根据IEEE-754 standard(读this overview),这是一个完全合法的舍入模式,它的术语是“向无穷大舍入”(或“从零开始”)。 9个downvotes中的大多数都不公平地击败了OP。是的,在本机Python中没有单一功能的方法来执行此操作,但我们可以使用round(float(a)/b)或子类numbers.Number并覆盖__div__()

OP需要澄清他们是否希望-3/2轮到-2或-1(或者不关心负操作数)。既然他们已经说过他们不想要向上舍入,我们可以推断-3/2应该舍入到-2。

足够的理论。对于实现:

  • 如果您只想要快速而肮脏的单线解决方案,请使用round(float(a)/b)
  • math.ceil(float(a)/b)向你提供向上,你说你不想要

  • 但是如果这是你的默认除法操作,或者你正在做很多这样的操作,那么请执行以下伪代码:inherit from one of the subclasses of numbers.Number Real, Rational or Integral (new in 2.6),重新定义__div__()或者定义非默认值替代__divra__()操作。您可以定义类成员或类方法rounding_mode并在分区期间查找它。注意__rdiv__()并混合普通花车。

import numbers

class NumberWithRounding(numbers.Integral):
    # Here you could implement a classmethod setRoundingMode() or member rounding_mode
    def __div__(self,other):
        # here you could consider value of rounding_mode, or else hardwire it like:
        return round(float(self)/other)
    # You also have to raise ImplementationError/ pass/ or implement the other 31
    # methods for Float: __abs__(),...,__xor__() Just shortcut that for now...

答案 3 :(得分:2)

分割两个整数时,结果为整数 3 / 2等于1,而不是1.5the documentation,注1:

  

对于(普通或长整数)除法,结果为整数。结果总是向负无穷大舍入:1/2为0,( - 1)/ 2为-1,1 /( - 2)为-1,(-1)/( - 2)为0.注意如果任一操作数是一个长整数,则结果为长整数,无论​​数值如何。

从分部获得1后,无法将其转换为2

要获得1.5,您需要浮点除法:3.0 / 2 然后,您可以致电math.ceil以获取2

你错了;没有分数的数学函数,然后四舍五入 你可以做的最好的事情就是编写自己的函数,它需要两个浮点函并调用math.ceil

答案 4 :(得分:1)

你可能想要的是:

math.ceil(3.0/2.0)
# or
math.ceil(float(3)/float(2))

您也可以从以后进行导入:

from __future__ import division
math.ceil(3/2) # == 2

但是,如果你这样做,要获得整数除法的当前行为,你需要使用双斜杠:

3 // 2 == 1 # True

答案 5 :(得分:1)

我认为您正在寻找的是:

假设你有x(3)和y(2),

result = (x + y - 1) // y;

这相当于不使用浮点的上限。

当然,y不能为0。

答案 6 :(得分:1)

gmpy2中提供了带有上限舍入(到+ Inf),向下舍入(到-Inf)和截断(到0)的整数除法。

>>> gmpy2.c_div(3,2)
mpz(2)
>>> help(gmpy2.c_div)
Help on built-in function c_div in module gmpy2:
c_div(...)
    c_div(x,y): returns the quotient of x divided by y. The quotient
    is rounded towards +Inf (ceiling rounding). x and y must be integers.
>>> help(gmpy2.f_div)
Help on built-in function f_div in module gmpy2:
f_div(...)
    f_div(x,y): returns the quotient of x divided by y. The quotient
    is rounded towards -Inf (floor rounding). x and y must be integers.
>>> help(gmpy2.t_div)
Help on built-in function t_div in module gmpy2:
t_div(...)
    t_div(x,y): returns the quotient of x divided by y. The quotient
    is rounded towards 0. x and y must be integers.
>>>

gmpy2可在http://code.google.com/p/gmpy/

获取

(免责声明:我是gmpy和gmpy2的当前维护者。)

答案 7 :(得分:0)

首先,您希望在参数中使用浮点除法。使用:

from __future__ import division

如果您总是希望向上舍入,f(3/2)==2f(1.4)==2,那么您希望fmath.trunc(math.ceil(x))

如果你想获得最接近的整数,但是有一些关系,那么你需要math.trunc(x + 0.5)。这样f(3/2)==2f(1.4)==1