【问题标题】:How do I round an integer down to the nearest multiple of a number?如何将整数向下舍入到最接近的数字倍数?
【发布时间】:2019-05-02 21:37:16
【问题描述】:

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

假设数字等于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) 的位置。

【问题讨论】:

  • “有没有比 x -= (x % the_number) 更有效的方法?”鉴于该操作是 O(1),您希望获得多高的效率?
  • x = (x / the_number) * the_number;
  • 你怎么知道它不是“高效”的?仅仅因为似乎有很多操作正在进行,并不意味着它效率低下。您是否查看过生成的机器代码或对其进行基准测试/配置文件/计时(启用优化)?
  • 我知道div指令会占用大量CPU时间。
  • @JL2210 你想如何处理负数?

标签: c rounding


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    我正在尝试将整数四舍五入为数字的最接近的倍数。 [...] 有没有比x -= (x % the_number) 更有效的方法?

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

    x = (x / the_number) * the_number
    

    ,但你不会用少于两个算术运算来做到这一点。 (另外- 在某些架构上比* 更高效,/% 通常在效率上差不多)。

    但是,如果您想截断到预先知道的 2 次幂,则可以通过使用单个位 &amp; 屏蔽低位来实现。例如,要截断到最接近的 16 的低倍数(== 0x10),你可以这样写

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

    【讨论】:

    • 您可以使用x &amp;= ~the_number 来处理任何数字吗?
    • 不,@JL2210。这不会产生截断为the_number 的倍数的效果,无论是在一般情况下还是在二次幂的情况下,除非可能是xthe_number 的非常具体的组合。如果您愿意,请尝试一些示例。
    【解决方案3】:

    只有一种方法可以检查:

    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 可能会更慢。

    【讨论】:

      【解决方案4】:

      如果您怀疑该值可能是 2 的幂,那么检查单个位除数并使用移位和掩码处理 mod 是值得的。少量比特的回报会递减,但可能是值得的。

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-25
        • 2012-03-07
        • 2012-01-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多