C取模运算中,当被除数和除数为负数时的差异是长无符号整数。

3

嗨,我知道在 C 中,负被除数的模数(modulo)会是负数,但我发现使用长无符号除数或长长无符号除数的负被除数的模数会是正数!

这里有个例子:

#include <stdio.h>

int main(int argc, char** argv)
{
    long long int a = -2205348223670655684LL;
    printf("%lld %lld %lld %lld %lld %lld\n", a % 20, a % 20L, a % 20LL, a % 20U, a % 20LU, a % 20LLU);
    return 0;
}

并且输出:

$> ./a.out
-4 -4 -4 -4 12 12

有人能解释一下为什么吗?我已经试过使用 GCC 4.8 和 5.1。


3
“负被除数的模除结果为负数”是错误的。这个结果取决于具体实现,不是固定的规定。 - n. m.
@user694733:这个被标准化了吗?我猜是C11吧?你能给出具体章节吗?(我真的很感兴趣。) - DevSolar
1
看看这个线程链接 - Jerome
2
@DevSolar N1570 6.5.5.6: *"如果商 a/b 可以表示,则表达式 (a/b)b + a%b 应等于 a;否则,a/b 和 a%b 的行为是未定义的。" - user694733
@user694733 哎呀,他们改了它。太不幸了。 - n. m.
显示剩余7条评论
2个回答

4
C99的6.5.5/6规定,当a/b可表示时:
(a/b) * b + a%b 必须等于a
而从6.5.5/3得知:
操作数执行常规算术转换。
有关算术转换的详细信息,请参见6.3.1.8节。
现在看起来,在您的实现中sizeof(long) = sizeof(long long) = 64位
对于前4种情况,有符号或无符号除数可以更改为分子(即long long int类型),但是在最后2种情况下,必须将被除数更改(强制转换或重新解释)为无符号类型,因为除数具有相同宽度并且是无符号的导致了结果。
在某些系统中,其中sizeof(long) < sizoef(long long),第二个最后的结果应该不同。

3

没有一种内置的算术操作可以执行一个有符号和一个无符号操作数;二进制操作的两个操作数都会被提升为相同的类型。

在您的最后两个示例中,该类型为unsigned long long;因此,a被转换为一个无符号值,并且使用该值计算余数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接