在 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 中的输出。我每次都期待一个积极的结果。模数可以是负数吗?任何人都可以解释这种行为吗?
C99要求whena/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
C 中的%
运算符不是取模运算符,而是余数运算符。
模数和余数运算符在负值方面有所不同。
对于余数运算符,结果的符号与被除数(分子)的符号相同,而对于模运算符,结果的符号与除数(分母)相同。
C 将%
操作定义a % b
为:
a == (a / b * b) + a % b
整数除法与/
截断朝向0
。这是针对0
(而不是针对负无穷大)进行的截断,将 定义%
为余数运算符而不是模运算符。
基于 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 中是余数运算而不是模运算。
我认为没有必要检查该数字是否为负数。
找到正模的一个简单函数是 -
编辑:假设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
.
如果它们也超过了 long long 的限制(即接近 LLONG_MAX),那么您应该按照此处其他答案中的描述分别处理正面和负面情况。
其他答案已在C99或更高版本中解释,涉及负操作数的整数除法总是截断为零。
请注意,在C89中,无论结果是否向上还是向下,都定义了。因为在所有标准中都(a/b) * b + a%b
等于,所以涉及负操作数的结果也在 C89 中实现定义。a
%
模数可以是负数吗?
%
可以是负数,因为它是余数运算符,除法后的余数,而不是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
根据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]:这通常被称为“向零截断”。
模运算的结果取决于分子的符号,因此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)
如需进一步阅读,请查看数学中的等价类。
模运算符给出余数。c中的模运算符通常采用分子的符号
此外,模数(余数)运算符只能用于整数类型,不能用于浮点数。
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
,而是余数。无论如何,关键是返回底片确实有意义。
看来问题是/
不是地板操作。
int mod(int m, float n)
{
return m - floor(m/n)*n;
}