繁体   English   中英

负数模运算

[英]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 > 0N + 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 乘法运算符

句法

  1. 乘法表达式:
    • cast-expression
    • multiplicative-expression * cast-expression
    • multiplicative-expression / cast-expression
    • multiplicative-expression % cast-expression

约束

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

语义

  1. 通常的算术转换是在操作数上执行的。

  2. 二元*运算符的结果是操作数的乘积。

  3. /运算符的结果是第一个操作数除以第二个操作数的商; %运算符的结果是余数。 在这两个操作中,如果第二个操作数的值为零,则行为未定义。

  4. 当整数被除时, /运算符的结果是代数商,其中任何小数部分都被丢弃 [1]。 如果商a/b是可表示的,则表达式(a/b)*b + a%b应等于a

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

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

这是参考

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 中的模运算符通常采用分子的符号

  1. x = 5 % (-3) - 这里的分子为正,因此结果为 2
  2. y = (-5) % (3) - 这里的分子为负,因此结果为 -2
  3. z = (-5) % (-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;
}

当数字为正数时,模运算符就像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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM