如何将整数四舍五入到最接近的整数倍数?

How do I round an integer down to the nearest multiple of a number?

我正在尝试将整数四舍五入为最接近的数字倍数。

假设数字等于80

有没有比x -= (x % the_number)更有效的方法?

这是一个例子:

#include <stdio.h>

int main(void)
{
        int x = 191;
        int the_number = 80;
        printf("x == %d\n", x);
        x -= (x % the_number);
        printf("x - (x %% the_number) == %d\n", x);
        return 0;
}

这是另一个例子。它不像以前那样是一个完整的工作程序,但它更清楚我正在尝试做什么:

#define LIGHT_GRAY 0x0700
#define COLUMNS 80

void pm_putchar(int c, void **ptr)
{
        c &= 0xff;
        c |= LIGHT_GRAY;

        if(c == '\n' | LIGHT_GRAY)
                *ptr += COLUMNS;
        else if(c == '\r' | LIGHT_GRAY)
                *ptr -= (*ptr % COLUMNS);

        **(short **)ptr = (short)c;
        (*ptr) += 2;
}

在上面的示例中,假设 *ptr 等于或高于 VGA 文本缓冲区 (0x0b8000) 的位置。

好吧,如果您只是尝试使用整数除数(您除以的数字),那么总有 ((int) x / theNumber) * theNumber),但它看起来是否更好取决于您。但是,我不知道有什么更好的方法,但是您可以重新利用 this thread.

上的某些答案。

I'm trying to round an integer to the nearest multiple of a number. [...] Is there a more efficient way to do this than x -= (x % the_number)?

一般情况下,不会。有类似效率的替代方案,例如

x = (x / the_number) * the_number

,但你不会用少于两次的算术运算来完成它。 (此外 - 在某些架构上比 * 更有效,并且 /% 通常在效率上相当)。

但是,如果您想截断为 2 的预先已知的幂,则可以通过使用单个按位 & 屏蔽掉低位来实现。例如,要截断到最接近的 16 的小倍数 (== 0x10),您可以编写

x &= ~0xf;  // truncate an int x to a multiple of 16

只有一种检查方法:

int r1(int r, const int n)
{
    return r -= (r % n);
}

int r2(int r, const int n)
{
    return (r / n) * n;
}

和生成的代码

r1:
        mov     eax, edi
        cdq
        idiv    esi
        mov     eax, edi
        sub     eax, edx
        ret
r2:
        mov     eax, edi
        cdq
        idiv    esi
        imul    eax, esi
        ret

div/mul 方法的指令较少,但考虑到延迟和执行时间,imul 可能会更慢。

如果您怀疑该值可能是 2 的幂,那么检查一位除数并使用移位和掩码处理 mod 是值得的。对于少量的位数,return 递减,但可能是值得的。

if (!(b & (b-1))) {
    x = a & (b-1);
} else {
    x = a % b;
}

我有点惊讶 cpu 没有简化这个简单的案例,它似乎在随机 64 位 mods 上产生了 4-5 的因子。