Python风格的整数除法和C中的模数

2022-09-01 14:41:34

在Python和Ruby中,有符号整数除法向负无穷大截断,有符号整数模数与第二个操作数具有相同的符号:

>>> (-41) / 3
-14
>>> (-41) % 3
1

但是,在 C 和 Java 中,有符号整数除法向 0 截断,并且有符号整数模数与第一个操作数具有相同的符号:

printf("%d\n", (-41) / 3); /* prints "-13" */
printf("%d\n", (-41) % 3); /* prints "-2" */

在C中执行与Python和Ruby相同的除法和模量的最简单,最有效的方法是什么?


答案 1

使用有符号整数除法舍入的方向在较旧的 C 标准中没有指定。但是,在 C99 中,它被指定为舍入为零。

以下是适用于所有版本的 C 标准和 CPU 架构的可移植代码:

int py_div(int a, int b)
{
  if (a < 0)
    if (b < 0)
      return -a / -b;
    else
      return -(-a / b) - (-a % b != 0 ? 1 : 0);
  else if (b < 0)
      return -(a / -b) - (a % -b != 0 ? 1 : 0);
    else
      return a / b;
}

int py_mod(int a, int b)
{
  if (a < 0)
    if (b < 0)
      return -(-a % -b);
    else
      return -a % b - (-a % -b != 0 ? 1 : 0);
  else if (b < 0)
      return -(a % -b) + (-a % -b != 0 ? 1 : 0);
    else
      return a % b;
}

我做了一些肤浅的测试,它似乎给出了与Python相同的结果。此代码可能不是最大效率,但是一个好的 C 编译器可能会充分优化它,特别是如果您将代码作为静态函数放在标头中。

您可能还想看看这个密切相关的问题:整数除法在C++中用负数舍入


答案 2

对于模,我发现下面最简单的。实现的符号约定是什么并不重要,我们只是将结果强制到我们想要的符号:

r = n % a;
if (r < 0) r += a;

显然,这是为了积极的一面。对于负数 a,您需要:

r = n % a;
if (r > 0) r += a;

这(也许有点令人困惑)组合在一起给出了以下内容(C++。在C中,用int做同样的事情,然后乏味地长时间写一个重复:

template<typename T> T sign(T t) { return t > T(0) ? T(1) : T(-1); }

template<typename T> T py_mod(T n, T a) {
    T r = n % a;
    if (r * sign(a) < T(0)) r += a;
    return r;
}

我们可以使用一个廉价的双值“sign”函数,因为我们已经知道a!=0,否则%将是未定义的。

将相同的原理应用于除法(查看输出而不是输入):

q = n / a;
// assuming round-toward-zero
if ((q < 0) && (q * a != n)) --q;

可以说,乘法可能比必要的更昂贵,但如果需要,可以在以后根据每个架构进行微优化。例如,如果你有一个除法操作,给你商和余数,那么你被排序为除法。

[编辑:可能存在一些边缘情况,例如,如果商或余数INT_MAX或INT_MIN。但是,无论如何,模拟Python对大值的数学是另一个问题;-)]

[另一个编辑:标准的python实现不是用C语言编写的吗?你可以搜索他们所做的事情的来源]