在c程序中,我正在尝试以下操作(只是检查行为)
x = 5 % (-3);
y = (-5) % (3);
z = (-5) % (-3);
printf("%d ,%d ,%d", x, y, z);
在gcc中输出为(2, -2 , -2)
。我每次都期待一个积极的结果。模数可以为负数吗?任何人都可以解释这种行为吗?
答案 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中的模数运算符通常采用分子的符号
模数(余数)运算符也只能与整数类型一起使用,不能与浮点一起使用。
答案 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乘法运算符
语法
- 乘法表达式:
cast-expression
multiplicative-expression * cast-expression
multiplicative-expression / cast-expression
multiplicative-expression % cast-expression
约束
- 每个操作数应具有算术类型。的 %运算符的操作数应为整数类型。
语义学
通常的算术转换是在 操作数。
二进制 * 运算符的结果是 操作数。
/ 运算符的结果是 第一个操作数除以第二个操作数;的 %运算符的结果为余数。同时 操作,如果第二个操作数的值为零, 行为是不确定的。
除以整数时, / 运算符的结果 是具有任何小数部分的代数商 丢弃[1]。如果商
a/b
是可表示的, 表达式(a/b)*b + a%b
等于a
。[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;
}