4

有人可以帮我弄清楚为什么当在这个 C 程序中输入一个负数时这个程序对模运算给出错误的答案吗?

我很确定导致问题的原因是scanf功能。使用正整数时给出正确答案。

代码如下:

#include <stdio.h>

int main()
{
    int num1 = 0;
    int num2 = 0;
    int answer = 0;
    puts("enter two number to find the modulus of...");
    if (scanf("%3d %3d",&num1,&num2) != 2)
            puts("something went wrong");
    else {
            answer = (num1 % num2);
            printf("the modulus of %d and %d is: %d\n", num1, num2, answer);
    }
}
4

2 回答 2

4

在 ANSI C 中,没有为负输入定义取模运算符结果的符号。div()您可以从数学库(ref)中尝试。它返回一个具有商和余数的结构,并且它对于负输入可靠地工作。

或者,正如 Alexey Frunze 所建议的那样,您可以启用 C99 模式。我懒得查标准,但是一点测试 ( gcc -std=c99) 表明结果的符号与左操作数的符号匹配。如果您想要 ANSI 兼容性,那么div()仍然是最好的。

或者,您可以完全控制局势。但是你必须选择正确的意思。以下内容来自维基百科

int x, y, q, r; // all snippets: left-arg, right-arg, quotient, remainder

截断除法

q = trunc( (double)x / y);
r = x - y * q;

地板师 (Knuth)

q = floor( (double)x / y);
r = x - y * q;

欧几里得除法

q = y > 0 ? floor( (double)x / y) : ceiling( (double)x / y);
r = x - y * q;
于 2013-04-26T07:26:08.570 回答
2

接受回答后

在 C 中,从 C99 开始,即使使用负操作数,结果%也可以很好地定义。见下文。

在 C 中,%运算符被命名为 C 中的“余数”而不是“模数”。

要执行欧几里得除法和余数,而不会产生截断、范围、double转换问题**:

void Euclidean_divide_remainder(int a, int b, int *q, int *r) {
  *q = a / b;
  *r = a % b;
  if (a < 0 && *r != 0) {
    if (b < 0) { (*q)++; *r -= b; }
    else {       (*q)--; *r += b; }
  }
}

void rtest(int a, int b) {
  int eq, er;
  Euclidean_divide_remainder(a, b, &eq, &er);
  printf("f(%2d,%2d) / %2d  %% %2d  E/ %2d  E%% %2d\n", 
      a, b, a / b, a % b, eq, er);
}

void rtest4(int a, int b) {
  rtest(a, b);
  rtest(a, -b);
  rtest(-a, b);
  rtest(-a, -b);
  printf("\n");
}

int main(void) {
  rtest4(7, 3);
  return 0;
}

f( 7, 3) /  2  %  1  E/  2  E%  1
f( 7,-3) / -2  %  1  E/ -2  E%  1
f(-7, 3) / -2  % -1  E/ -3  E%  2
f(-7,-3) /  2  % -1  E/  3  E%  2

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

当整数被除法时, / 运算符的结果是代数商,其中任何小数部分被丢弃。(这通常称为“向零截断”。)如果商a/b是可表示的,则表达式(a/b)*b + a%b应等于a; 否则,两者的行为a/ba%b是未定义的。C11dr §6.5.5 6

** 例外情况:

的结果a%0是未定义的。

对于 2 的补码,INT_MIN % -1INT_MIN E% -1 (在数学上应该是 0)是问题。这是由于INT_MIN / -1(在数学上应该是INT_MAX + 1)是一个问题,因为答案不适合该int范围。

于 2014-08-28T19:02:56.947 回答