在C程序中,我正在尝试以下操作(只需检查行为)
x = 5 % (-3);
y = (-5) % (3);
z = (-5) % (-3);
printf("%d ,%d ,%d", x, y, z);
给我的输出与(2, -2 , -2)
gcc中的一样。我每次都期待一个积极的结果。模数可以为负吗?有人可以解释这种行为吗?
在C程序中,我正在尝试以下操作(只需检查行为)
x = 5 % (-3);
y = (-5) % (3);
z = (-5) % (-3);
printf("%d ,%d ,%d", x, y, z);
给我的输出与(2, -2 , -2)
gcc中的一样。我每次都期待一个积极的结果。模数可以为负吗?有人可以解释这种行为吗?
Answers:
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
-5/3
,-2
并且mod变为1。简而言之:一个模块的符号跟随除数符号(截断),另一个模块的符号跟随除数符号(Knuth)。
%
C中的运算符不是模运算符,而是余数运算符。
模和余数运算符在负值方面有所不同。
对于余数运算符,结果的符号与除数的符号相同,而对于模运算符,结果的符号与除数相同。
C将%
操作定义a % b
为:
a == (a / b * b) + a % b
与/
该整数除法与向截断0
。这就是朝着0
(而不是朝着负无穷大)进行的截断,将其定义%
为余数运算符而不是取模运算符。
remainder
并且modulo
在Scheme rem
和mod
Haskell中)。这些运算符的规范在这些语言的划分方式上有所不同:截断为0或负无穷大。C标准从来没有调用方式%
的模运算,他们只是它命名的 %操作。
remainder
功能在C,其与在分割圆入,向最近的语义实现IEEE其余
基于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语言中的运算是余数运算,而不是模运算。
b
为负数时,它不会给出正结果(实际上,对于r
和b
均为负数,其结果都小于-b
)。为了确保您可以使用所有输入的正结果r + abs(b)
或匹配b
s符号,可以将条件r*b < 0
改为。
r + abs(b)
当@MartinEnder 为UB时b == INT_MIN
。
我认为没有必要检查该数字是否为负。
查找正模的简单函数是:
编辑:假设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。
long long int
,或单独处理否定情况(以失去简单性为代价)。
模数可以为负吗?
%
可以是负数,因为它是余数运算符,除法后的余数,而不是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
模运算的结果取决于分子的符号,因此对于y和z,您得到-2
这是参考
http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_14.html
整数部
本节介绍执行整数除法的功能。这些功能在GNU C库中是多余的,因为在GNU C中,“ /”运算符总是四舍五入。但是在其他C实现中,“ /”可能会以否定的取舍方式不同。div和ldiv很有用,因为它们指定了如何将商数四舍五入为零。其余部分与分子的符号相同。
在这些约定源于的数学中,没有断言求模运算应该产生肯定的结果。
例如。
1 mod 5 = 1,但也可以等于-4。也就是说,1/5从0产生余数1或从5产生-4。(均为5的因子)
同样,-1 mod 5 = -1,但也可以等于4。也就是说,-1 / 5从0产生余数-1或从-5产生4。(均为5倍)
为了进一步阅读,请研究数学中的等价类。
a
和b
,b <> 0
。根据欧几里得除法定理,恰好存在一对整数m
,r
其中a = m * b + r
和0 <= r < abs( b )
。所说的r
是(数学)模运算的结果,根据定义是非负的。Wikipedia上的更多阅读内容和更多链接:en.wikipedia.org/wiki/Euclidean_division
1 mod 5
总是1。-4 mod 5
也可能是1,但它们是不同的东西。
根据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乘法运算符
句法
- 乘法表达式:
cast-expression
multiplicative-expression * cast-expression
multiplicative-expression / cast-expression
multiplicative-expression % cast-expression
约束条件
- 每个操作数应具有算术类型。%运算符的操作数应为整数类型。
语义学
通常的算术转换是对操作数执行的。
二进制*运算符的结果是操作数的乘积。
/运算符的结果是第一个操作数除以第二个的商。%运算符的结果是余数。在这两个操作中,如果第二个操作数的值为零,则行为是不确定的。
当整数相除时,/运算符的结果为代数商,其中任何小数部分都被舍弃[1]。如果商
a/b
是可表示的,则表达式(a/b)*b + a%b
应等于a
。[1]:这通常称为“向零截断”。