[英]Modulo operation with negative numbers
在 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
=> (-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
with /
向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;
}
我的结论是 C 中的a % b
是余数运算而不是模运算。
我认为没有必要检查数字是否为负数。
一个简单的函数来找到正模数是这样的 -
编辑:假设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
在所有标准中都等于a
,所以%
涉及负操作数的结果在 C89 中也是实现定义的。
模数可以为负吗?
%
可以是负数,因为它是余数运算符,除法后的余数,而不是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]:这通常被称为“向零截断”。
模运算的结果取决于分子的符号,因此你得到 -2 的y和z
这是参考
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;
}
当数字为正数时,模运算符就像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加减法也一样。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.