在C程序中,我正在尝试以下操作(只需检查行为)

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

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


将gcc的输出显示为(2, -2 , -2)。我每次都期待一个积极的结果。模数可以为负吗?有人可以解释这种行为吗?

评论

stackoverflow.com/questions/4003232 / ...的可能重复项
具有负值的Modulo运算符的可能重复项

#1 楼

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
时,这种情况才会发生

评论


编译器是否应该足够聪明,并且检测到一个无符号对另一个无符号取模始终为正?当前(好在GCC 5.2),编译器似乎认为在这种情况下,即使两个操作数都为uint32_t或更大,“%”也返回“ int”,而不是“ unsigned”。

– Frederick Nord
16-09-24在14:56

@FrederickNord您是否有显示该行为的示例?

–chux-恢复莫妮卡
17-2-24在16:16



了解您所描述的是mod的常规int(a / b)(截断)描述。但是规则也可能是floor(a / b)(Knuth)。在Knuth情况下,-5 / 3为-2,并且mod变为1。简而言之:一个模块的符号后面跟着除数符号(截断),另一个模块的符号后面跟着除数符号(Knuth)。

–艾萨克
18年5月27日在9:36

这是C标准完全不是我想要的情况。我从未想过将其截断为零或负模数,但是我经常想截取相反的数并且需要解决C问题。

–乔
18年8月6日在22:27

#2 楼

C中的%运算符不是模运算符,而是余数运算符。

模和余数运算符在负值方面有所不同。

对于余数运算符,结果的符号与除数的符号相同,而对于模运算符,结果的符号与除数的符号相同。

C将%a % b运算定义为:

  a == (a / b * b) + a % b


其中/是对0进行截断的整数除法。这是对0(而不是对负无穷大)的截断,将%定义为余数运算符而不是取模运算符。

评论


根据定义,余数是模运算的结果。不应存在​​余数运算符,因为不存在余数运算,这被称为模。

–gronostaj
15年6月27日在20:49

@gronostaj不在CS中。查看诸如Haskell或Scheme的高级语言,它们都定义了两个不同的运算符(Scheme中的余数和模数,Haskell中的rem和mod)。这些运算符的规格在这些语言的划分方式上有所不同:截断为0或负无穷大。顺便说一下,C标准从不将%称为模运算符,他们只是将其命名为%运算符。

–哇
15年12月21日在16:55

不要与C中的余数函数相混淆,后者在除法中以舍入为最近的语义实现IEEE余数

–埃里克
17 Sep 18'2:11



#3 楼

基于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是余数运算,而不是模运算。

评论


当b为负数时,这不会给出正结果(实际上,对于r和b均为负数,其结果都小于-b)。为了确保所有输入的结果都是正面的,可以使用r + abs(b)或匹配bs符号,可以将条件改为r * b <0。

–马丁·恩德(Martin Ender)
16-09-20在11:42

当b == INT_MIN时,@ MartinEnder r + abs(b)为UB。

–chux-恢复莫妮卡
18/09/27在4:20

#4 楼

我认为没有必要检查数字是否为负。
查找正模的简单函数就是这个-
编辑:假设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
如果它们也越过很长的界限(即LLONG_MAX附近),则您应按照此处其他答案所述分别处理正面和负面情况。

评论


请注意,当N <0时,结果可能为modulo(7,-3)-> -2。 x%N + N也会溢出int数学,这是未定义的行为。例如modulo(INT_MAX-1,INT_MAX)可能会产生-3。

–chux-恢复莫妮卡
18-09-27在4:17

是的,在那种情况下,您可以简单地使用long long int,或者单独处理否定情况(以失去简单性为代价)。

– Udayraj Deshmukh
18-10-7在14:08

#5 楼

其他答案已经在C99或更高版本中得到了解释,涉及负操作数的整数除法总是截断为零。

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

#6 楼


模数可以为负数吗?


%可以为负,因为它是余数运算符,除法后的余数,而不是Euclidean_division之后的余数。由于C99,结果可能为0,可以是负数或正数。

 // a % b
 7 %  3 -->  1  
 7 % -3 -->  1  
-7 %  3 --> -1  
-7 % -3 --> -1  


所需的模运算运算符是经典的欧几里德模数,而不是%


我每次都期望得到一个阳性结果。


要执行定义了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   


#7 楼

模运算的结果取决于分子的符号,因此,对于y和z,您得到-2。

这里是参考文献

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


整数除法

本节描述了执行整数除法的函数。 br />这些函数在GNU C库中是多余的,因为在GNU C中,'/'运算符总是四舍五入。但是在其他C
实现中,'/'可能会用负数进行舍入。
div和ldiv很有用,因为它们指定了如何将
商数舍入为零。其余与
分子的符号相同。


评论


您指的是有关ANSI C的文本。这是C的相当老的规范。不确定该文本对ANSI C是否正确,但对于C99绝对不正确。在C99§6.5.5中,整数除法定义为始终截断为零。

– Palec
2014年2月5日在20:36

#8 楼

在这些约定源于的数学中,没有断言求模运算应该产生肯定的结果。

例如。

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

类似地,

为了进一步阅读,请研究数学的等价类。

评论


等效类是一个不同的概念,并且以非常严格的方式定义了模。假设我们有两个整数a和b,b <>0。根据欧几里得除法则,恰好存在一对整数m,r,其中a = m * b + r和0 <= r
–酯
18年5月9日在6:54

那不是真的1 mod 5始终为1。-4 mod 5也可能为1,但它们是不同的东西。

–FelipeC
19年5月5日在7:35

#9 楼

根据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乘法运算符

语法


乘法-expression:


cast-expression
multiplicative-expression * cast-expression
multiplicative-expression / cast-expression
multiplicative-expression % cast-expression




约束


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

语义


通常的算术转换是对
操作数执行的。
二进制*运算符的结果是
操作数的乘积。
/运算符的结果是
第一个操作数除以第二个操作数的商。 %运算符的
结果为余数。在两个
操作中,如果第二个操作数的值为零,则
行为是不确定的。
除以整数时,/运算符的结果
是代数商并将任何小数部分
丢弃[1]。如果a/b的商是可表示的,则
(a/b)*b + a%b表达式应等于a

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


#10 楼


c中的模运算符通常取分子的符号


x = 5%(-3)-此处的分子为正,因此导致2
y =(-5)%(3)-这里的分子为负数,因此结果为-2
z =(-5)%(-3)-这里的分子为负数,因此结果为-2

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

评论


如果您可以通过外部资源链接进行备份,那就太好了。

–... J ... S
17年8月26日在16:13

#11 楼

我认为将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,而是余数。无论如何,关键是返回负数确实有意义。

#12 楼

看来问题在于/不是地面操作。
int mod(int m, float n)
{  
  return m - floor(m/n)*n;
}