在一个c程序中,我正在尝试以下操作(只是为了检查行为)
x = 5 % (-3);
y = (-5) % (3);
z = (-5) % (-3);
printf("%d ,%d ,%d", x, y, z);
在gcc中给了我输出(2, -2 , -2)
。我每次都期待一个积极的结果。模数可以为负数吗?任何人都可以解释这种行为吗?
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
时才会发生
根据http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_14.html,6.5.5乘法运算符,需要以下内容:
equivalence classes
根据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
句法
- 乘法表达:
(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
约束
- 每个操作数都应具有算术类型。 %运算符的操作数应具有整数类型。
语义
- 通常的算术转换是在操作数上执行的。
- binary *运算符的结果是操作数的乘积。
- /运算符的结果是第一个操作数除以第二个操作数的商; %运算符的结果是余数。在这两个操作中,如果第二个操作数的值为零,则行为未定义。
- 当整数被划分时,/运算符的结果是代数商,丢弃任何小数部分[1]。如果商
multiplicative-expression / cast-expression
是可表示的,则表达式multiplicative-expression % cast-expression
应等于a/b
。[1]:这通常被称为“截断为零”。
模数运算符与数字为正数时的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加法和减法相同。
我认为将(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
,它是剩下的。无论如何,重点是返回底片确实有意义。
C中的(-5)%3
运算符不是模运算符,而是余数运算符。
模数和余数运算符在负值方面不同。
对于余数运算符,结果的符号与被除数的符号相同,而对于模运算符,结果的符号与除数相同。
C将-2
的%
操作定义为:
%
与a % b
整数划分与截断朝向 a == (a / b * b) + a % b
。这是对/
(而不是负向无效)的截断,它将0
定义为余数运算符而不是模运算符。
基于C99规范:0
我们可以写一个函数来计算%
!
a = (a / b) * b + a % b
对于模运算,我们可以有以下函数(假设b> 0)
(a % b) = a - (a / b) * b
我的结论是(a%b)在C中是余数运算符而非模运算符。
我认为没有必要检查数字是否为负数。
找到正模的一个简单函数就是这个 -
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),那么你应该分别处理正面和负面情况,如其他答案所述。
其他答案已在C99或更高版本中解释,涉及负操作数的整数除法总是截断为零。
请注意,在C89中,向上或向下的结果是实现定义的。因为int
在所有标准中等于long long int
,所以涉及负操作数的(a/b) * b + a%b
的结果也在C89中实现定义。
模数可以为负数吗?
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
模运算的结果取决于分子的符号,因此y和z得到-2
这是参考
a,b
整数部
本节介绍执行整数除法的功能。这些函数在GNU C库中是多余的,因为在GNU C中,'/'运算符总是向零舍入。但在其他C实现中,'/'可能与负参数不同。 div和ldiv很有用,因为它们指定了如何舍入商:向零。余数与分子具有相同的符号。
在数学中,这些约定来自于数学,没有断言模运算应该产生积极的结果。
例如。
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
。
模数运算符给出余数。 c中的模数运算符通常采用分子的符号
模数(余数)运算符也只能与整数类型一起使用,不能与浮点一起使用。