对负数的模运算

在一个C程序中,我尝试了以下操作(只是为了检查行为)

 x = 5 % (-3);
y = (-5) % (3);
z = (-5) % (-3);


printf("%d ,%d ,%d", x, y, z);

它在gcc中输出为(2, -2 , -2)。我以为每次都会有积极的结果。模量可以是负的吗?有人能解释一下这种行为吗?

335832 次浏览

模运算的结果取决于分子的符号,因此yz的结果是-2

这是参考资料

http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_14.html

整数的除法

介绍整数除法的函数。 这些函数在GNU C库中是多余的,因为在GNU C中 '/'运算符总是四舍五入到零。但是在其他C中 实现中,'/'可以用不同的负参数四舍五入。 Div和ldiv很有用,因为它们指定了如何舍入 商:趋于零。余数的符号和 分子。< / p >

C99 需要,当a/b是可表示的:

(a/b) * b + a%b等于a

从逻辑上讲,这是有道理的。对吧?

让我们看看这会导致什么:


例A. 5/(-3)-1

=> __abc0 __abc3 __abc1 = __abc2

这只能在5%(-3)为2时发生。


例B. (-5)/3-1

=> __abc0 __abc3 __abc1 = __abc2

这只有在(-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;
}

我的结论是,C语言中的a % b是一个余数运算,而不是一个模运算。

其他答案已在C99或后续版本中解释,涉及负操作数的整数除法总是向零截断

注意,在C89中,结果是向上舍入还是向下舍入是由实现定义的。因为在所有标准中(a/b) * b + a%b等于a,所以包含负操作数的%的结果也是在C89中实现定义的。

我认为没有必要检查数字是否为负。

求正模的一个简单函数是这个-

编辑:假设N > 0N + N - 1 <= INT_MAX

int modulo(int x,int N){
return (x % N + N) %N;
}

这将适用于x的正负皆有值。

原注:也如@chux指出的,如果你的x和N可能分别达到INT_MAX-1和INT_MAX,只需用long long int替换int

如果它们也越过了long long的限制(即在LLONG_MAX附近),那么你应该分别处理正的和负的情况,在这里的其他答案中描述。

在数学中,这些惯例的起源,没有断言模算术应该产生一个正的结果。

如。

1 mod 5 = 1,但也可以等于-4。也就是说,1/5从0得到余数1或从5得到余数-4。(都是5的因数)

< p >相似的, -1 mod 5 = -1,它也可以等于4。也就是说,-1/5从0得到余数-1或从-5得到余数4。(两个因子都是5)

要进一步阅读,请参阅数学中的等价类

模运算符给出余数。 c中的模算子通常取分子的符号

  1. X = 5%(-3)这里分子是正的,所以结果是2
  2. Y =(-5) %(3)分子为负,结果为-2
  3. Z =(-5) %(-3)这里分子是负的所以结果是-2

此外,模(余数)运算符只能用于整型,不能用于浮点数。

根据C99标准, section 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乘法运算符

语法

    <李> multiplicative-expression:
    • cast-expression
    • multiplicative-expression * cast-expression
    • multiplicative-expression / cast-expression
    • multiplicative-expression % cast-expression
    • 李< / ul > < / >

约束

  1. 每个操作数都应具有算术类型。的 操作符的操作数必须为整数类型

语义

  1. 通常的算术转换是在 李操作数。< / p > < / > 二进制操作符的结果是的乘积 李操作数。< / p > < / > /操作符的结果是from的商 第一个操作数除以第二个操作数;的 操作符的结果是余数。在这两个 操作,如果第二个操作数的值为零,

  2. .行为未定义
  3. 整数除法时,/运算符的结果 代数商有小数部分吗 丢弃的[1]。如果商a/b是可表示的, 表达式(a/b)*b + a%b等于a

[1]:这通常被称为“向零截断”。

模量可以是负的吗?

%可以是负数,因为它是剩下的操作符,除法后的余数,而不是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

我相信将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;
}