在C中获得最小非负残差模n的有效方法是什么?

是否有一种有效的方法可以得到最小的非负残差模n,其中n是正的,在C?

如果数字是非负数,那么这很容易,那么它只是%n(其中a是非负整数)。

但是当a为负数时,C89中的行为似乎是实现定义的(感谢kennyTM)。 即-2%11 = -2或9。

此外,在C99中,行为被定义为恼人的行为:-2%11 = -2。

一般来说(即,当m不是常数且n的范围不受约束时, n % m ),你可能无法做到比通常更好

 res = ((n % m) + m) % m 

将它与您平台上的以下内容进行比较可能会很有趣; 一个分支可能会赢得额外的模数:

 res = n % m; if (res < 0) res += m; 

您只需检查结果是否为负数,然后采取相应措施:

 int mod(int n, int m) { int r = n % m; if (r < 0) return r + m; else return r; } 

或者,没有if-then-else和单个表达式:

 r = ((n % m) + m) % m; 

怎么样

 if (a > 0) return a % n; if (a < 0) { r = n - (-a % n); if (r == n) return 0; return r; } 

如果a <0,那么r = -a % n是[0,n]中的值,使得对于某个整数k,k * n + r = -a。 然后n - r是(0,n)中的值,并且因为-r = a + k * n,我们有n - r = a +(k + 1)* n,或者a =(n - r)+ (-k - 1)* n。由此可以看出n - r是a的模数,因为它在(0,n)中,所以它是非负的。

最后,您希望结果在[0,n]范围内,而不是在(0,n)范围内。为了确保这一点,我们检查r是否为n,如果是,则返回0.(当然,模数 - n-相当于n)

很少有处理器在硬件中实现余数,而是通过除法和乘法合成。 从机器的角度来看,这实际上并不是一个繁琐的重新实现:

 int qm = n / m * m; // a positive or negative multiple of m, rounded up or down if ( qm <= n ) return n - qm; // usual definition of % else return n - qm + m; // most common definition of -%, with adjustment 

条件+微优化也可能是有益的。 这可能会在您的计算机上更快或更慢,但它可以工作:

 int rem = n - n / m * m; return rem + m & -( rem < 0 ); 

总成本:一个常规模数加一个右移(生成-(rem<0) ),一个按位,一个添加。