向上翻n / 4的有效方法

时间:2015-02-03 17:41:36

标签: c algorithm math optimization rounding

我有一个整数n,我需要向上舍入n / 4。出于性能原因,我需要在C中找到一个快速的方法。除以4可以使用>>来完成。 2轮换班,但我不知道这一轮。我可以使用ceil,但我担心性能。

2 个答案:

答案 0 :(得分:8)

如果你的操作数是非负的,那么:

unsigned int
roundupdiv4 (unsigned int n)
{
    return (n+3)>>2;
}

请注意,任何合理的编译器都会将/4的{​​{1}}编译为unsigned int

我可以通过使用>>2编译上述内容来确认:

gcc -O3 -S
如果我将 .file "x.c" .text .p2align 4,,15 .globl roundupdiv4 .type roundupdiv4, @function roundupdiv4: .LFB0: .cfi_startproc leal 3(%rdi), %eax shrl $2, %eax ret .cfi_endproc .LFE0: .size roundupdiv4, .-roundupdiv4 .ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2" .section .note.GNU-stack,"",@progbits 替换为>>2

并注意输出完全相同

另请注意,我使用/4作为unsigned int是为负签名左操作数定义的实现(即向右移动负值)。如果你想要一个可以用于签名值的工作(严格地说):

>>

因为整数除法使用截断舍入,即无论如何舍入为负数(朝向零)。 (那是C99 onwards;它是在C89中定义的实现。)

如果通过四舍五入你的意思是“从零开始”,那么:

int
roundupdiv4 (int n)
{
    return ((n>0)?(n+3):n)/4;
}

答案 1 :(得分:3)

这会优化为7条指令,包括使用gcc -O3在我的x86_64机器上返回。

int div4roundup(int x)
{
   return (x & 3) ? (x >> 2) + 1 : (x>>2);
}

拆卸:

int div4roundup(int x)
{
  return (x & 3) ? (x >> 2) + 1 : (x>>2);
   0:   89 f8                   mov    %edi,%eax
   2:   31 d2                   xor    %edx,%edx
   4:   c1 f8 02                sar    $0x2,%eax
   7:   83 e7 03                and    $0x3,%edi
   a:   0f 95 c2                setne  %dl
   d:   01 d0                   add    %edx,%eax
}
   f:   c3                      retq   

与abligh的等效解决方案相比:

int
roundupdiv4 (int n)
{
    return ((n>0)?(n+3):n)/4;
   0:   85 ff                   test   %edi,%edi
   2:   8d 47 03                lea    0x3(%rdi),%eax
   5:   7f 05                   jg     c <fc+0xc>
   7:   85 ff                   test   %edi,%edi
   9:   0f 49 c7                cmovns %edi,%eax
   c:   c1 f8 02                sar    $0x2,%eax
}
   f:   c3                      retq   

与abligh的替代解决方案相比:

0000000000000000 <roundawayfromzerodiv4>:
int
roundawayfromzerodiv4 (int n)
{
    return ((n>0)?(n+3):(n-3))/4;
   0:   85 ff                   test   %edi,%edi
   2:   7e 0c                   jle    10 <roundawayfromzerodiv4+0x10>
   4:   8d 47 03                lea    0x3(%rdi),%eax
   7:   c1 f8 02                sar    $0x2,%eax
   a:   c3                      retq   
   b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  10:   89 f8                   mov    %edi,%eax
  12:   83 e8 03                sub    $0x3,%eax
  15:   0f 48 c7                cmovs  %edi,%eax
  18:   c1 f8 02                sar    $0x2,%eax
}
  1b:   c3                      retq   

编辑:以为我想出了比其他答案更快的东西然后意识到我正在比较两个略有不同的计算。我们的两个“舍入严格”功能在指令数上是相同的,但略有不同。没有分析拆卸足以知道哪个更快。