c – (n * 2-1)%p:当n和p为32位时,避免使用64位
作者:互联网
考虑以下功能:
inline unsigned int f(unsigned int n, unsigned int p)
{
return (n*2-1)%p;
}
现在假设n(和p)大于std :: numeric_limits< int> :: max().
例如f(4294967295U,4294967291U).
数学结果为7,但函数将返回2,因为n * 2将溢出.
然后解决方案很简单:我们只需要使用64位整数.假设函数的声明必须保持不变:
inline unsigned int f(unsigned int n, unsigned int p)
{
return (static_cast<unsigned long long int>(n)*2-1)%p;
}
一切都好.至少在原则上.问题是这个函数在我的代码中会被调用数百万次(我的意思是溢出版本),64位模数比32位版本慢(例如参见here).
问题如下:是否存在任何技巧(数学或算法)以避免执行64位版本的模数运算.什么是使用这个技巧的新版本的f? (保持相同的声明).
>注1:n> 0
>注2:p> 2
>注3:n可以低于p:n = 4294967289U,p = 4294967291U
>注4:使用的模数运算次数越少越好(3 32位模数太大,2很有意思,1肯定会超越)
>注5:当然结果将取决于处理器.假设在最后的至强可用的超级计算机上使用.
解决方法:
即使我不喜欢处理AT& T语法和GCC的“扩展asm约束”,我认为这是有效的(它在我的工作中,无可否认有限,测试)
uint32_t f(uint32_t n, uint32_t p)
{
uint32_t res;
asm (
"xorl %%edx, %%edx\n\t"
"addl %%eax, %%eax\n\t"
"adcl %%edx, %%edx\n\t"
"subl $1, %%eax\n\t"
"sbbl $0, %%edx\n\t"
"divl %1"
: "=d"(res)
: "S"(p), "a"(n)
:
);
return res;
}
我不知道,这些限制可能是不必要的严格或错误.它似乎工作.
这里的想法是进行常规的32位分割,实际上需要64位的分红.它仅在商符合32位(否则发出溢出信号)时有效,在这种情况下总是如此(p至少为2,n不为零).除法之前的东西处理时间2(溢出到edx,“高半”),然后“减1”处理潜在的借位. “= d”输出的东西使得剩余的结果. “a”(n)将n放入eax(让它选择另一个寄存器没有帮助,除法将在edx:eax中输入). “S”(p)可能是“r”(p)(似乎有效),但我不确定是否相信它.
标签:modulus,c,c11,algorithm,math 来源: https://codeday.me/bug/20190829/1764446.html