C中的溢流安全模块化加法和减法?

时间:2012-06-28 15:33:03

标签: c algorithm math optimization

我在C中实现了一个算法,它需要在无符号整数上快速进行模数加法和减法,并能正确处理溢出条件。这就是我现在所拥有的(它确实有效):

/* a and/or b may be greater than m */
uint32_t modadd_32(uint32_t a, uint32_t b, uint32_t m) {
    uint32_t tmp;
    if (b <= UINT32_MAX - a)
        return (a + b) % m;

    if (m <= (UINT32_MAX>>1))
        return ((a % m) + (b % m)) % m;

    tmp = a + b;
    if (tmp > (uint32_t)(m * 2)) // m*2 must be truncated before compare
        tmp -= m;
    tmp -= m;
    return tmp % m;
}

/* a and/or b may be greater than m */
uint32_t modsub_32(uint32_t a, uint32_t b, uint32_t m) {
    uint32_t tmp;
    if (a >= b)
        return (a - b) % m;

    tmp = (m - ((b - a) % m)); /* results in m when 0 is needed */
    if (tmp == m)
        return 0;
    return tmp;
}

有人知道更好的算法吗?我发现模块化算法的库似乎都适用于大的任意精度数,这种方法有点过分。

编辑:我希望这能在32位机器上运行良好。此外,我现有的函数可以简单地转换为其他大小的无符号整数,这是一个很好的保留属性。

3 个答案:

答案 0 :(得分:9)

模块化操作通常假设a和b小于m。这允许更简单的算法:

umod_t sub_mod(umod_t a, umod_t b, umod_t m)
{
  if ( a>=b )
    return a - b;
  else
    return m - b + a;
}

umod_t add_mod(umod_t a, umod_t b, umod_t m)
{
  if ( 0==b ) return a;

  // return sub_mod(a, m-b, m);
  b = m - b;
  if ( a>=b )
    return a - b;
  else
    return m - b + a;
}

资料来源:Matters Computational,第39.1章。

答案 1 :(得分:1)

如果它适合,我会在uint32_t中进行算术,否则在uint64_t进行算术。

uint32_t modadd_32(uint32_t a, uint32_t b, uint32_t m) {
    if (b <= UINT32_MAX - a)
        return (a + b) % m;
    else
        return ((uint64_t)a + b) % m;
}

在具有64位整数类型的体系结构上,这应该几乎没有开销,您甚至可以考虑只在uint64_t中执行所有操作。在合成uint64_t的体系结构上  让编译器决定他认为最好的东西,然后查看生成的汇编程序并测量是否满意。

答案 2 :(得分:0)

  

溢出安全的模块化添加

首先使用通常a<m确定b<m% m

添加更新的ab

如果a(或b)超过uintN_t总和,那么数学上的和是uintN_t溢出和减去m将“mod”在数学上总和到uintN_t的范围。

如果总和超过m,那么就像上面的步骤一样,m的单个减法将“修改”总和。

uintN_t modadd_N(uintN_t a, uintN_t b, uintN_t m) {
  // may omit these 2 steps if a < b and a < m are known before the call.
  a %= m;
  b %= m;

  uintN_t sum = a + b;
  if (sum >= m || sum < a) {
    sum -= m;
  }
  return sum;
}

最后非常简单。

  

溢出安全模块减法

@Evgeny Kluev良好答案的变化。

uintN_t modsub_N(uintN_t a, uintN_t b, uintN_t m) {
  // may omit these 2 steps if a < b and a < m are known before the call.
  a %= m;
  b %= m;

  uintN_t diff = a - b;  
  if (a < b) {
    diff += m;
  }
  return diff;
}

请注意,此方法适用于各种N,例如32, 64, 16unsignedunsigned long等,而无需使用更广泛的类型。它也适用于比int/unsigned窄的无符号类型。