带负数的模运算

时间:2012-07-30 11:31:22

标签: c gcc modulo

在c程序中,我正在尝试以下操作(只是检查行为)

 x = 5 % (-3);
 y = (-5) % (3);
 z = (-5) % (-3); 

printf("%d ,%d ,%d", x, y, z); 

在gcc中输出为(2, -2 , -2)。我每次都期待一个积极的结果。模数可以为负数吗?任何人都可以解释这种行为吗?

13 个答案:

答案 0 :(得分:137)

C99 要求表示a/b可表示:

(a/b) * b + a%b等于a

这在逻辑上是有道理的。对吧?

让我们看看这会导致什么:


示例A. 5/(-3)-1

=> (-1) * (-3) + 5%(-3) = 5

只有在5%(-3)为2时才会发生这种情况。


示例B. (-5)/3-1

=> (-1) * 3 + (-5)%3 = -5

仅当(-5)%3-2

时才会发生这种情况

答案 1 :(得分:116)

C中的%运算符不是模数运算符,而是余数运算符。

模数和余数运算符在负值方面有所不同。

使用余数运算符,结果的符号与被除数的符号相同,而对于模运算符,结果的符号与除数相同。

C将%的{​​{1}}操作定义为:

a % b

a == (a / b * b) + a % b 整数除法,截断为/。这是针对0(而不是负面无效)的截断,它将0定义为余数运算符而不是模运算符。

答案 2 :(得分:57)

基于C99规范:a = (a / b) * b + a % b

我们可以编写一个函数来计算(a % b) = a - (a / b) * b

int remainder(int a, int b)
{
    return a - (a / b) * b;
}

对于模运算,我们可以有以下函数(假设b> 0)

int mod(int a, int b)
{
    int r = a % b;
    return r < 0 ? r + b : r;
}

我的结论是(a%b)在C中是余数运算符而非模运算符。

答案 3 :(得分:42)

我认为没有必要检查数字是否为负数。

找到正模的一个简单函数就是这个 -

int modulo(int x,int N){
    return (x % N + N) %N;
}

假设N为正,这将适用于 x的正负值。

P.S。同样如@chux所指出的,如果您的x和N分别达到INT_MAX-1和INT_MAX,只需将int替换为long long int

如果他们也超过了长期的限制(即接近LLONG_MAX),那么你将分别处理正面和负面情况,如其他答案所述。

答案 4 :(得分:7)

其他答案已在 C99 或更高版本中解释,涉及负操作数的整数除法总是截断为零

请注意,在 C89 中,向上或向下舍入的结果是否是实现定义的。由于(a/b) * b + a%b在所有标准中等于a,因此涉及负操作数的%的结果也是在C89中实现定义的。

答案 5 :(得分:5)

  

模量可以为负吗?

%可以是负数,因为它是remainder operator,除法后的余数,而不是Euclidean_division之后的余数。由于C99,结果可能为0,可以是负数或正数。

 // a % b
 7 %  3 -->  1  
 7 % -3 -->  1  
-7 %  3 --> -1  
-7 % -3 --> -1  

所需的 modulo OP是经典的Euclidean modulo,而不是%

  

我每次都期待一个积极的结果。

要执行定义了a/b时定义良好的欧氏模,a,b可以有任何符号,并且结果永远不能为负:

int modulo_Euclidean(int a, int b) {
  int m = a % b;
  if (m < 0) {
    // m += (b < 0) ? -b : b; // avoid this form: it is UB when b == INT_MIN
    m = (b < 0) ? m - b : m + b;
  }
  return m;
}

modulo_Euclidean( 7,  3) -->  1  
modulo_Euclidean( 7, -3) -->  1  
modulo_Euclidean(-7,  3) -->  2  
modulo_Euclidean(-7, -3) -->  2   

答案 6 :(得分:3)

模数运算的结果取决于分子的符号,因此你得到-2 y z

这是参考资料

http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_14.html

  

整数部门

     

本节介绍执行整数除法的功能。   这些函数在GNU C库中是多余的,因为在GNU C中   '/'运算符总是向零舍入。但在其他C中   实现,'/'可能与负参数不同。   div和ldiv很有用,因为它们指定了如何舍入   商:朝零。其余的符号与   分子。

答案 7 :(得分:2)

在这些惯例源于的数学中,没有断言模运算会产生正结果。

EG。

1 mod 5 = 1,但它也可以等于-4。也就是说,1/5从0开始产生0或-4的余数1。(5的两个因子)

类似地, -1 mod 5 = -1,但它也可以等于4.也就是说,-1 / 5从0得到余数-1或从-5得到4。 (两个因素都是5)

进一步阅读数学中的equivalence classes

答案 8 :(得分:1)

模数运算符给出余数。 c中的模数运算符通常采用分子的符号

  1. x = 5%( - 3) - 此处分子为正,因此导致2
  2. y =( - 5)%(3) - 此处分子为负,因此结果为-2
  3. z =( - 5)%( - 3) - 此处分子为负,因此得到-2
  4. 模数(余数)运算符也只能与整数类型一起使用,不能与浮点一起使用。

答案 9 :(得分:1)

根据C99 standard,第 6.5.5节 乘法运算符,必须满足以下条件:

(a / b) * b + a % b = a

结论

余数运算结果的符号 到C99,与股息相同。

让我们看一些示例(dividend / divisor):

当只有股息为负时

(-3 / 2) * 2  +  -3 % 2 = -3

(-3 / 2) * 2 = -2

(-3 % 2) must be -1

当除数为负时

(3 / -2) * -2  +  3 % -2 = 3

(3 / -2) * -2 = 2

(3 % -2) must be 1

当除数和除数均为负时

(-3 / -2) * -2  +  -3 % -2 = -3

(-3 / -2) * -2 = -2

(-3 % -2) must be -1

  

6.5.5乘法运算符

     

语法

     
      
  1. 乘法表达式:      
        
    • cast-expression
    •   
    • multiplicative-expression * cast-expression
    •   
    • multiplicative-expression / cast-expression
    •   
    • multiplicative-expression % cast-expression
    •   
  2.   
     

约束

     
      
  1. 每个操作数应具有算术类型。的   运算符的操作数应为整数类型。
  2.   
     

语义学

     
      
  1. 通常的算术转换是在   操作数。

  2.   
  3. 二进制 * 运算符的结果是   操作数。

  4.   
  5. / 运算符的结果是   第一个操作数除以第二个操作数;的   运算符的结果为余数。同时   操作,如果第二个操作数的值为零,   行为是不确定的。

  6.   
  7. 除以整数时, / 运算符的结果   是具有任何小数部分的代数商   丢弃[1]。如果商a/b是可表示的,   表达式(a/b)*b + a%b等于a

  8.   
     

[1]:这通常称为“向零截断”。

答案 10 :(得分:0)

模数运算符与数字为正数时的mod运算符类似,但如果数字为负数则不同。

在问题中很多次我们被要求以10 ^ 9 + 7的模式给出答案。

让答案(在使用模数之前)用&#39; a&#39;表示。

简单明了的规则 -

如果a ,那么模10 ^ 9 + 7 = a%(10 ^ 9 + 7)

如果a ,则模10 ^ 9 + 7 = (a%(10 ^ 9 + 7))+(10 ^ 9 + 7)< / EM>

如果在这些问题中,我们发现循环的任何步骤都可能计算出超出整数范围的值(如果我们使用整数),那么我们可以在该步骤中使用模运算符。最后的答案就好像我们只使用了模运算符一次。

这是因为 - (a * b)%c =((a%c)(b%c))%c加法和减法相同。

答案 11 :(得分:0)

我认为将mod定义为抽象算术会更有用;不是作为一个运算,而是作为一个整体,不同类别的算术,具有不同的元素和不同的运算符。这意味着mod 3中的加法与“普通”加法不同;那是;整数加法。

因此,当您这样做时:

5 % -3

您正在尝试将整数 5映射到mod -3集中的元素。这些是mod -3的元素:

{ 0, -2, -1 }

所以:

0 => 0, 1 => -2, 2 => -1, 3 => 0, 4 => -2, 5 => -1

假设您由于某种原因必须熬夜30个小时,那一天您还剩下多少个小时? 30 mod -24

但是C实现的不是mod,而是余数。无论如何,关键是返回负数确实有意义。

答案 12 :(得分:0)

看来问题在于/不是 floor 操作。

int mod(int m, float n)
{  
  return m - floor(m/n)*n;
}