在 Python 中,整数除法中向零舍入的好方法是啥?

Posted

技术标签:

【中文标题】在 Python 中,整数除法中向零舍入的好方法是啥?【英文标题】:In Python, what is a good way to round towards zero in integer division?在 Python 中,整数除法中向零舍入的好方法是什么? 【发布时间】:2013-11-12 01:41:40 【问题描述】:
1/2

给予

0

应该如此。然而,

-1/2

给予

-1

,但我希望它向 0 舍入(即我希望 -1/2 为 0),无论它是正数还是负数。最好的方法是什么?

【问题讨论】:

int(1./2) 应该可以工作。 主题中的问题很清楚,但示例具有误导性。 1 / 2 和 -1 /2 在 Python 中分别给出 0.5 和 -0.5,而不是 0。此外,函数 round() 给出了示例中要求的内容: round(1 / 2) = round( -1 / 2 ) = 0,因为当 Python 在两个相等接近的整数之间进行选择时,它会四舍五入到一个偶数。 @Dominic108 在 python2 中,1 / 2 == 0-1 / 2 == -1 【参考方案1】:

进行浮点除法然后转换为整数。不需要额外的模块。

Python 3:

>>> int(-1 / 2)
0
>>> int(-3 / 2)
-1
>>> int(1 / 2)
0
>>> int(3 / 2)
1

Python 2:

>>> int(float(-1) / 2)
0
>>> int(float(-3) / 2)
-1
>>> int(float(1) / 2)
0
>>> int(float(3) / 2)
1

【讨论】:

这在 2 和 3 中应该是一样的,不需要从 __future__ 导入任何东西,对吧? 是的,这种行为应该在 2 和 3 之间保持一致。 注意:使用float 仅在 Python 2 中有意义。它在 Python 3 中没有用处。 对于 Python3 只需使用:int(a/b)【参考方案2】:

Python 的默认整数除法是返回下限(朝向负无穷大),但无法更改。你可以readBDFL的原因。

要进行“向上”除法,您可以使用:

>>> a=1
>>> b=2
>>> (a+(-a%b))//b
1
>>> a,b=-1,2
>>> (a+(-a%b))//b
0

要向零截断并保持整数除法,如果 a 或 b 为负数,则使用 (a+(-a%b))//b,如果两者都是正数,则使用默认除法。

这将进行整数除法并始终向零舍入:

>>> a=1
>>> b=2
>>> a//b if a*b>0 else (a+(-a%b))//b
0
>>> a=-1
>>> b=2
>>> a//b if a*b>0 else (a+(-a%b))//b
0
>>> a,b=-3,2
>>> a//b if a*b>0 else (a+(-a%b))//b
-1
>>> a,b=3,2
>>> a//b if a*b>0 else (a+(-a%b))//b
1

脚注

有趣的是,C99 声明 round towards zero 是默认值:

#include <stdio.h>
int main(int argc, const char * argv[])


    int a=-3;
    int b=2;
    printf("a=%d, b=%d, a/b=%d\n",a,b,a/b);
    a=3;
    printf("a=%d, b=%d, a/b=%d\n",a,b,a/b);
    return 0;

打印:

a=-3, b=2, a/b=-1
a=3, b=2, a/b=1

【讨论】:

当向零舍入时,是否有理由更喜欢上述方法而不是 int(a / b)【参考方案3】:

对于它的价值,我自己最喜欢的解决方案是这个。仅整数运算,单除法,其他一切都是线性时间:

def integer_divide_towards_zero(a, b):
    return -(-a // b) if a < 0 else a // b

假设b 是肯定的,但在我见过的大多数应用程序中,这是正确的。如果你也需要处理否定的b,那么函数会稍微复杂一些:

def integer_divide_towards_zero(a, b):
    return -(-a // b) if (a < 0) ^ (b < 0) else a // b

一些示例输出:

>>> integer_divide_towards_zero(11, 3)
3
>>> integer_divide_towards_zero(-11, 3)
-3
>>> integer_divide_towards_zero(6, 3)
2
>>> integer_divide_towards_zero(-6, 3)
-2
>>> integer_divide_towards_zero(11, -3)
-3
>>> integer_divide_towards_zero(-11, -3)
3

【讨论】:

【参考方案4】:

既然有了完美的 math.trunc() 函数,为什么还要重新发明***?

import math
print(math.trunc(-3.5))
>>-3
print(math.trunc(3.5))
>>3

【讨论】:

【参考方案5】:

试试这个。仅适用于大于 -1 的数字

import math

x = .5
y = -.5

print math.floor(math.fabs(x))
>> 0

print math.floor(math.fabs(y))
>> 0

【讨论】:

这没有给出正确的答案。 -1.5 应该给出 -1,但这给出 +1。【参考方案6】:

在我看来,执行此操作的正确代码太晦涩难懂,无法编写为 1-liner。所以我会把它放在一个函数中,比如:

def int0div(a, b):
    q = a // b
    if q < 0 and b*q != a:
        q += 1
    return q

良好的特性:它适用于任何大小的 int,除非必要,否则不对原始 (a//b) 结果进行任何调整,只进行一次除法(% 也可以在幕后进行除法),并且不会创建任何大于输入的整数。这些在您的申请中可能很重要,也可能无关紧要;如果您使用“大”整数,它们会变得更加重要(对于速度而言)。

【讨论】:

【参考方案7】:

用一些替代的想法来表达我的想法:

将数字 [abs(x)/x] 的符号乘以 abs(x)/2

(abs(x)/x)*(abs(x)/2)

执行加法,但如果数字小于零,则加一以使其更接近 0。

x/2 + int(x<0)

【讨论】:

这会损害可读性,但有趣的是,您可以在此处省略对 int 的调用 - x/2 + x&lt;0 工作正常(因为 bool 子类 int 和没有重新定义算术运算的工作方式)。 @lvc 过去我遇到过int + bool 变成bool 的问题,因为您可以将int 转换为bool,反之亦然。我只是觉得更明确一些会更好。【参考方案8】:

您还可以将Decimal 模块用作标准python 库的一部分。

具体来说, " 整数除法运算符 // 行为类似,返回真商的整数部分(向零截断)而不是其底,以保持通常的恒等式 x == (x // y) * y + x % y :"

>>> -7 // 4
-2
>>> Decimal(-7) // Decimal(4)
Decimal('-1')

另外,请查看 Rounding Modes,因为他们有很多方法可以查看/四舍五入您的信息 - 天花板、向下、地板、半向下、半均匀、半向上、向上和 05 向上舍入.

Decimal 是作为传统二进制数学问题的解决方案而编写的,在这个世界需要小数解决方案

【讨论】:

以上是关于在 Python 中,整数除法中向零舍入的好方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

无符号与有符号整数的性能

大整数的有底除法和欧几里得除法

Java:从零舍入的函数?

右移代替除以 2 的幂

在 C/C++ 中获得正模的最快方法

为啥整数除法在许多脚本语言中舍入?