我有一个整数n,我需要向上舍入n / 4。出于性能原因,我需要在C中找到一个快速的方法。除以4可以使用>>来完成。 2轮换班,但我不知道这一轮。我可以使用ceil,但我担心性能。
答案 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
编辑:以为我想出了比其他答案更快的东西然后意识到我正在比较两个略有不同的计算。我们的两个“舍入严格”功能在指令数上是相同的,但略有不同。没有分析拆卸足以知道哪个更快。