带负数的模运算

问题描述 投票:164回答:13

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

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

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

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

c gcc modulo
13个回答
141
投票

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

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

从逻辑上讲,这是有道理的。对?

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


例A.5/(-3)-1

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

这只有在5为2时才会发生。


例B.5%(-3)(-5)/3

=> Qazxswpoi -1 + = (-1) * 3

这只有在(-5)%3-5时才会发生


1
投票

根据http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_14.html,6.5.5乘法运算符,需要以下内容:

equivalence classes

Conclusion

根据C99,余数运算结果的符号与红利的结果相同。

让我们看一些例子(C99 standard):

只有股息是负数

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

只有除数是负数

dividend / divisor

当除数和被除数都是负数时

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

(-3 / 2) * 2 = -2

(-3 % 2) must be -1

6.5.5 Multiplicative operators

句法

  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 cast-expression multiplicative-expression * cast-expression

约束

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

语义

  1. 通常的算术转换是在操作数上执行的。
  2. binary *运算符的结果是操作数的乘积。
  3. /运算符的结果是第一个操作数除以第二个操作数的商; %运算符的结果是余数。在这两个操作中,如果第二个操作数的值为零,则行为未定义。
  4. 当整数被划分时,/运算符的结果是代数商,丢弃任何小数部分[1]。如果商multiplicative-expression / cast-expression是可表示的,则表达式multiplicative-expression % cast-expression应等于a/b

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


0
投票

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

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

让答案(在使用模数之前)用'a'表示。

简单明了的规则 -

如果a是正数,则模10 ^ 9 + 7 = a%(10 ^ 9 + 7)

如果a是负数,则模10 ^ 9 + 7 =(a%(10 ^ 9 + 7))+(10 ^ 9 + 7)

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

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


0
投票

我认为将(a/b)*b + a%b视为抽象算术中定义的更有用;不是作为一个操作,而是作为一个整体不同的算术类,具有不同的元素和不同的运算符。这意味着a中的添加与“正常”添加不同;那是;整数加法。

所以当你这样做时:

mod

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

mod -3

所以:

mod -3

假设你必须熬夜30个小时,那天你还剩多少小时? { 0, -2, -1 }

但是C实现的不是0 => 0, 1 => -2, 2 => -1, 3 => 0, 4 => -2, 5 => -1 ,它是剩下的。无论如何,重点是返回底片确实有意义。


122
投票

C中的(-5)%3运算符不是模运算符,而是余数运算符。

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

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

C将-2%操作定义为:

%

a % b整数划分与截断朝向 a == (a / b * b) + a % b 。这是对/(而不是负向无效)的截断,它将0定义为余数运算符而不是模运算符。


58
投票

基于C99规范:0

我们可以写一个函数来计算%

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

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

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

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


50
投票

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

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

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

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

附:同样如@chux所指出的,如果x和N分别达到INT_MAX-1和INT_MAX,只需用int mod(int a, int b) { int r = a % b; return r < 0 ? r + b : r; } 替换int modulo(int x,int N){ return (x % N + N) %N; } 即可。

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


8
投票

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

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


5
投票

模数可以为负数吗?

a可能是负面的,因为它是%,分裂后的余数,而不是%之后。由于C99,结果可能是0,负数或正数。

remainder operator

模数OP想要的是经典的Euclidean_division,而不是 // a % b 7 % 3 --> 1 7 % -3 --> 1 -7 % 3 --> -1 -7 % -3 --> -1

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

为了执行定义Euclidean modulo时定义良好的欧几里德模,%具有任何符号,结果永远不会为负:

a/b

3
投票

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

这是参考

a,b

整数部

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


2
投票

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

例如。

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

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

如需进一步阅读,请参阅数学中的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


1
投票

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

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

模数(余数)运算符也只能与整数类型一起使用,不能与浮点一起使用。

© www.soinside.com 2019 - 2024. All rights reserved.