259

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

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

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

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

4

12 回答 12

217

C99要求whena/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

于 2012-07-30T11:43:34.550 回答
204

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

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

对于余数运算符,结果的符号与被除数(分子)的符号相同,而对于模运算符,结果的符号与除数(分母)相同。

C 将%操作定义a % b为:

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

整数除法与/截断朝向0。这是针对0(而不是针对负无穷大)进行的截断,将 定义%为余数运算符而不是模运算符。

于 2012-07-30T11:50:50.140 回答
88

基于 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 中是余数运算而不是模运算。

于 2013-10-10T06:08:19.423 回答
79

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

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

编辑:假设N > 0N + N - 1 <= INT_MAX

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

这适用于x 的正值和负值

原始 PS:也正如@chux 所指出的,如果您的 x 和 N 可能分别达到 INT_MAX-1 和 INT_MAX 之类的值,只需替换intlong long int.

如果它们也超过了 long long 的限制(即接近 LLONG_MAX),那么您应该按照此处其他答案中的描述分别处理正面和负面情况。

于 2017-02-09T08:30:55.777 回答
10

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

请注意,在C89中,无论结果是否向上还是向下,都定义了。因为在所有标准中都(a/b) * b + a%b等于,所以涉及负操作数的结果也在 C89 中实现定义。a%

于 2015-06-17T07:02:26.890 回答
9

模数可以是负数吗?

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

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

所需的OP 是经典的欧几里得模,而不是%.

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

要执行无论何时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   
于 2018-09-27T04:26:32.223 回答
5

根据C99 标准,第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

约束

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

语义

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

  2. 二元*运算符的结果是操作数的乘积。

  3. /运算符的结果是第一个操作数除以第二个操作数的商;%运算符的结果是余数。在这两种操作中,如果第二个操作数的值为零,则行为未定义。

  4. 当整数相除时,/运算符的结果是代数商,其中任何小数部分都被丢弃 [1]。如果商a/b是可表示的,则表达式(a/b)*b + a%b应等于a

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

于 2018-08-16T14:57:14.113 回答
2

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

这是参考

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

整数除法

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

于 2012-07-30T11:42:40.230 回答
2

在数学中,这些约定源于数学,没有断言模算术应该产生积极的结果。

例如。

1 mod 5 = 1,但也可以等于 -4。也就是说,1/5 从 0 中产生余数 1 或从 5 中产生 -4。(两个因数均为 5)

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

如需进一步阅读,请查看数学中的等价类

于 2017-04-09T22:46:23.813 回答
1

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

  1. x = 5 % (-3) - 这里的分子是正数,因此结果为 2
  2. y = (-5) % (3) - 这里的分子是负数,因此它的结果是 -2
  3. z = (-5) % (-3) - 这里的分子是负数,因此它的结果是 -2

此外,模数(余数)运算符只能用于整数类型,不能用于浮点数。

于 2017-08-26T11:55:57.140 回答
1

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,而是余数。无论如何,关键是返回底片确实有意义。

于 2019-06-05T08:02:33.063 回答
0

看来问题是/不是地板操作。

int mod(int m, float n)
{  
  return m - floor(m/n)*n;
}
于 2020-07-20T12:51:21.907 回答