x = 5 % (-3);
y = (-5) % (3);
z = (-5) % (-3);
printf("%d ,%d ,%d", x, y, z);
将gcc的输出显示为
(2, -2 , -2)
。我每次都期待一个积极的结果。模数可以为负吗?有人可以解释这种行为吗?#1 楼
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
时,这种情况才会发生
评论
编译器是否应该足够聪明,并且检测到一个无符号对另一个无符号取模始终为正?当前(好在GCC 5.2),编译器似乎认为在这种情况下,即使两个操作数都为uint32_t或更大,“%”也返回“ int”,而不是“ unsigned”。
– Frederick Nord
16-09-24在14:56
@FrederickNord您是否有显示该行为的示例?
–chux-恢复莫妮卡
17-2-24在16:16
了解您所描述的是mod的常规int(a / b)(截断)描述。但是规则也可能是floor(a / b)(Knuth)。在Knuth情况下,-5 / 3为-2,并且mod变为1。简而言之:一个模块的符号后面跟着除数符号(截断),另一个模块的符号后面跟着除数符号(Knuth)。
–艾萨克
18年5月27日在9:36
这是C标准完全不是我想要的情况。我从未想过将其截断为零或负模数,但是我经常想截取相反的数并且需要解决C问题。
–乔
18年8月6日在22:27
#2 楼
C中的%
运算符不是模运算符,而是余数运算符。模和余数运算符在负值方面有所不同。
对于余数运算符,结果的符号与除数的符号相同,而对于模运算符,结果的符号与除数的符号相同。
C将
%
的a % b
运算定义为: a == (a / b * b) + a % b
其中
/
是对0
进行截断的整数除法。这是对0
(而不是对负无穷大)的截断,将%
定义为余数运算符而不是取模运算符。评论
根据定义,余数是模运算的结果。不应存在余数运算符,因为不存在余数运算,这被称为模。
–gronostaj
15年6月27日在20:49
@gronostaj不在CS中。查看诸如Haskell或Scheme的高级语言,它们都定义了两个不同的运算符(Scheme中的余数和模数,Haskell中的rem和mod)。这些运算符的规格在这些语言的划分方式上有所不同:截断为0或负无穷大。顺便说一下,C标准从不将%称为模运算符,他们只是将其命名为%运算符。
–哇
15年12月21日在16:55
不要与C中的余数函数相混淆,后者在除法中以舍入为最近的语义实现IEEE余数
–埃里克
17 Sep 18'2:11
#3 楼
基于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;
}
我的结论是C中的
a % b
是余数运算,而不是模运算。评论
当b为负数时,这不会给出正结果(实际上,对于r和b均为负数,其结果都小于-b)。为了确保所有输入的结果都是正面的,可以使用r + abs(b)或匹配bs符号,可以将条件改为r * b <0。
–马丁·恩德(Martin Ender)
16-09-20在11:42
当b == INT_MIN时,@ MartinEnder r + abs(b)为UB。
–chux-恢复莫妮卡
18/09/27在4:20
#4 楼
我认为没有必要检查数字是否为负。查找正模的简单函数就是这个-
编辑:假设
N > 0
和N + 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之类的值,则只需替换
int
如果使用long long int
。如果它们也越过很长的界限(即LLONG_MAX附近),则您应按照此处其他答案所述分别处理正面和负面情况。
评论
请注意,当N <0时,结果可能为modulo(7,-3)-> -2。 x%N + N也会溢出int数学,这是未定义的行为。例如modulo(INT_MAX-1,INT_MAX)可能会产生-3。
–chux-恢复莫妮卡
18-09-27在4:17
是的,在那种情况下,您可以简单地使用long long int,或者单独处理否定情况(以失去简单性为代价)。
– Udayraj Deshmukh
18-10-7在14:08
#5 楼
其他答案已经在C99或更高版本中得到了解释,涉及负操作数的整数除法总是截断为零。请注意,在C89中,结果是向上还是向下舍入是实现定义的。因为在所有标准中
(a/b) * b + a%b
都等于a
,所以在C89中也实现了定义为负数的%
的结果。#6 楼
模数可以为负数吗?
%
可以为负,因为它是余数运算符,除法后的余数,而不是Euclidean_division之后的余数。由于C99,结果可能为0,可以是负数或正数。 // a % b
7 % 3 --> 1
7 % -3 --> 1
-7 % 3 --> -1
-7 % -3 --> -1
所需的模运算运算符是经典的欧几里德模数,而不是
%
。我每次都期望得到一个阳性结果。
要执行定义了
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
#7 楼
模运算的结果取决于分子的符号,因此,对于y和z,您得到-2。这里是参考文献
http://www.chemie .fu-berlin.de / chemnet / use / info / libc / libc_14.html
整数除法
本节描述了执行整数除法的函数。 br />这些函数在GNU C库中是多余的,因为在GNU C中, />'/'运算符总是四舍五入。但是在其他C
实现中,'/'可能会用负数进行舍入。
div和ldiv很有用,因为它们指定了如何将
商数舍入为零。其余与
分子的符号相同。
评论
您指的是有关ANSI C的文本。这是C的相当老的规范。不确定该文本对ANSI C是否正确,但对于C99绝对不正确。在C99§6.5.5中,整数除法定义为始终截断为零。
– Palec
2014年2月5日在20:36
#8 楼
在这些约定源于的数学中,没有断言求模运算应该产生肯定的结果。例如。
1 mod 5 = 1,但也可以等于-4。也就是说,1/5从0产生余数1或从5产生-4。(都是5的因数)
类似地,
为了进一步阅读,请研究数学的等价类。
评论
等效类是一个不同的概念,并且以非常严格的方式定义了模。假设我们有两个整数a和b,b <>0。根据欧几里得除法则,恰好存在一对整数m,r,其中a = m * b + r和0 <= r
–酯
18年5月9日在6:54
那不是真的1 mod 5始终为1。-4 mod 5也可能为1,但它们是不同的东西。
–FelipeC
19年5月5日在7:35
#9 楼
根据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乘法运算符
语法
乘法-expression:
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 楼
c中的模运算符通常取分子的符号
x = 5%(-3)-此处的分子为正,因此导致2
y =(-5)%(3)-这里的分子为负数,因此结果为-2
z =(-5)%(-3)-这里的分子为负数,因此结果为-2
模数(余数)运算符只能用于整数类型,不能用于浮点运算。
评论
如果您可以通过外部资源链接进行备份,那就太好了。
–... J ... S
17年8月26日在16:13
#11 楼
我认为将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 楼
看来问题在于/
不是地面操作。int mod(int m, float n)
{
return m - floor(m/n)*n;
}
评论
stackoverflow.com/questions/4003232 / ...的可能重复项具有负值的Modulo运算符的可能重复项