我可以依靠 C 中的 % (模)运算符来处理负数吗?

Posted

技术标签:

【中文标题】我可以依靠 C 中的 % (模)运算符来处理负数吗?【英文标题】:Can I rely on % (modulo) operator in C for negative numbers? 【发布时间】:2015-05-24 14:50:27 【问题描述】:

使用 GCC:

printf("%i \n", -1 % (int)4);
printf("%u \n", -1 % (unsigned int)4);

输出:

-1
3

我可以跨平台依赖这种行为吗?我是否应该明确定义 MODREM 宏以确保不会更改?

【问题讨论】:

【参考方案1】:

模运算符 (%) 多年来一直是 C 和 C++ 标准的一部分。我不确定您是否可以在 C++ 中重载它。所以是的,你可以依赖它。

【讨论】:

根据这个答案,行为是用 C++ 定义的实现,还是已经过时了? ***.com/a/4003287/3467349 问题是:该代码会在 every 符合 C 编译器上产生相同的输出吗?因为 C 中有些东西(允许)依赖于编译器.. 好吧,我一直在编写纯 C 代码很长时间了。我使用过 *nix、MS、Borland 和 Intel C 编译器,除了 YMMV 之外从未出现过故障。 OP 没有询问您是否可以重载% 运算符。并且您不能重载 C 中的任何运算符。他询问负数的行为【参考方案2】:

从 C99 开始,% 的结果需要向 0 舍入,正如 Chris Dodd 所引用的那样。

在 C99 标准之前,% 运算符对负数的行为是实现定义的

当整数被除且除法不精确时,如果两个操作数都是正数,/ 运算符的结果是小于代数商的最大整数,% 运算符的结果是正数。 如果任一操作数为负数/ 运算符的结果是小于代数商的最大整数还是大于代数商的最小整数是实现定义的 >,就像% 运算符的结果的符号一样。如果商a/b 是可表示的,则表达式(a/b)*b + a%b 应等于a

Does either ANSI C or ISO C specify what -5 % 10 should be?

因此,如果您的目标是 C99 或更高版本,则结果是 Yes,否则您不能依赖它。

如果您需要可移植到更旧的 C 标准的一致结果,您可以使用 div or ldiv,无需定义自己的 MODREM

C99 rationale regarding div, ldiv, and lldiv functions:

因为当涉及负操作数时,C89 具有实现定义的有符号整数除法语义,所以发明了 div 和 ldiv 以及 C99 中的 lldiv 来为有符号整数除法和余数运算提供明确指定的语义。

【讨论】:

【参考方案3】:

C99 标准说:

6.5.5 乘法运算符

当整数被除法时,/ 运算符的结果是任何一个的代数商 小数部分被丢弃87)。如果商 a/b 是可表示的,则表达式(a/b)*b + a%b 应等于 a

87) 这通常被称为“向零截断”

这意味着除法总是向 0 舍入,所以你可以依赖它。

请注意,这与 C++03 标准不同。

您的第二行执行无符号除法,将值 -1 转换为除法之前的 unsigned int。这总是比 2 的幂小一,所以这也是很好定义的。

【讨论】:

它与 C++11 标准没有什么不同。您指的是哪个 C++ 标准? (或者,换句话说,它也不同于以前的 C 标准) @rici:我指的是 C++03 标准,它或多或少与 C99 标准并行。 C11/C++11 发生了很大变化 @user3467349: 奇怪的是,这保证评估为真——(unsigned int) 演员表的存在会将所有其他常量转换为无符号整数,-1s 将变得很大正整数(小于 2 的幂) 不同标准之间存在一种相对论时移效应,因此 C++03 和 C99 有点同时代的事实并不意味着它们实际上是一致的,尽管有一些好的意图。 (其中一些更改仍然超出了事件视界。)尽管如此,C++03 并没有排除% 的 C99 行为,尽管标准允许更大的灵活性,但大多数实现都是一致的. 你需要-std=c99,因为有些编译器默认不启用C99。这不适用于旧编译器

以上是关于我可以依靠 C 中的 % (模)运算符来处理负数吗?的主要内容,如果未能解决你的问题,请参考以下文章

java怎么计算一个整数长度(不含负数符号)?

c语言,求一个数的逆的模n运算等于多少!

Python中负数的模运算

Python笔记-取模运算%

c语言整数取整和取模运算

python中的负数取模问题(一个大坑)