其他分享
首页 > 其他分享> > c – (n * 2-1)%p:当n和p为32位时,避免使用64位

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