C / C ++中整数除法的快速上限

Fast ceiling of an integer division in C / C++

给定整数值xy,C和C ++都返回商q = x/y作为浮点等价的底限。 我对一种返回天花板的方法很感兴趣。 例如,ceil(10/5)=2ceil(11/5)=3

显而易见的方法包括:

1
2
q = x / y;
if (q * y < x) ++q;

这需要额外的比较和乘法; 和我见过的其他方法(事实上使用)涉及铸造floatdouble。 是否有更直接的方法可以避免额外的乘法(或第二个除法)和分支,并且还可以避免作为浮点数进行转换?


对于正数

1
unsigned int x, y, q;

围捕......

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

或(避免x + y溢出)

1
q = 1 + ((x - 1) / y); // if x != 0


对于正数:

1
    q = x/y + (x % y != 0);


Sparky的答案是解决这个问题的一种标准方法,但正如我在评论中所写,你冒着溢出的风险。这可以通过使用更宽的类型来解决,但是如果要分割long long s怎么办?

Nathan Ernst的答案提供了一个解决方案,但它涉及函数调用,变量声明和条件,这使得它不比OP代码短,甚至可能更慢,因为它更难以优化。

我的解决方案是:

1
q = (x % y) ? x / y + 1 : x / y;

它会比OPs代码略快,因为模数和除法是在处理器上使用相同的指令执行的,因为编译器可以看到它们是等价的。至少gcc 4.4.1在x86上使用-O2标志执行此优化。

从理论上讲,编译器可能会在Nathan Ernst的代码中内联函数调用并发出相同的内容,但是当我测试它时gcc没有这样做。这可能是因为它会将编译后的代码绑定到标准库的单个版本。

最后一点,在现代机器上,这一点都不重要,除非你处于一个非常紧凑的循环中并且所有数据都在寄存器或L1缓存中。否则所有这些解决方案都会同样快,除了可能是Nathan Ernst之外,如果必须从主存储器获取该函数,这可能会明显变慢。


您可以使用cstdlib中的div函数来获取商和&amp;剩下一个电话,然后单独处理天花板,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <cstdlib>
#include <iostream>

int div_ceil(int numerator, int denominator)
{
        std::div_t res = std::div(numerator, denominator);
        return res.rem ? (res.quot + 1) : res.quot;
}

int main(int, const char**)
{
        std::cout <<"10 / 5 =" << div_ceil(10, 5) << std::endl;
        std::cout <<"11 / 5 =" << div_ceil(11, 5) << std::endl;

        return 0;
}


这个怎么样? (要求y为非负数,所以在极少数情况下不要使用它,其中y是一个没有非负性保证的变量)

1
q = (x > 0)? 1 + (x - 1)/y: (x / y);

我将y/y减少到1,消除了术语x + y - 1,并且随之出现溢出的可能性。

x是无符号类型且包含零时,我避免x - 1环绕。

对于带符号x,负数和零仍然合并为一个案例。

对于现代通用CPU来说可能不是一个巨大的好处,但在嵌入式系统中,这比任何其他正确的答案要快得多。


有一个正面和负面x的解决方案,但仅适用于只有1个分区而没有分支的正y

1
2
3
int ceil(int x, int y) {
    return x / y + (x % y > 0);
}

注意,如果x为正,那么除法为零,如果提醒不为零,我们应该加1。

如果x为负,则除法为零,这就是我们需要的,我们不会添加任何东西,因为x % y不是正数


这适用于正数或负数:

1
q = x / y + ((x % y != 0) ? !((x > 0) ^ (y > 0)) : 0);

如果有余数,则检查xy是否具有相同的符号并相应地添加1


简化的通用形式,

1
2
3
int div_up(int n, int d) {
    return n / d + (((n < 0) ^ (d > 0)) && (n % d));
} //i.e. +1 iff (not exact int && positive result)

对于更通用的答案,C ++用于整数除法,具有良好定义的舍入策略


我宁可评论,但我没有足够高的代表。

据我所知,+ ve&amp; pow of 2这是最快的方式(在CUDA中测试)

1
2
//example y=8
q = x >> 3 + !!(x & 7);

否则(也只是+ ve)我倾向于这样做:

1
q = x/y + !!(x % y);

使用O3编译,编译器可以很好地执行优化。

1
2
q = x / y;
if (x % y)  ++q;