'%' 在 C 中是表示“mod”还是“rem”?
在 C 中,% 是余数1。
...,/ 运算符的结果是丢弃任何小数部分的代数商...(这通常称为“向零截断”。)C11dr §6.5.5 6
% 运算符的操作数应为整数类型。 C11dr §6.5.5 2
/ 运算符的结果是第一个操作数除以第二个的商; % 运算符的结果是 remainder ... C11dr §6.5.5 5
“mod”和“remainder”有什么区别?
C 没有定义“mod”或“模”运算符/函数,例如 Euclidean division 或 other modulo 中使用的整数模函数。
C 定义余数。
让我们将% 运算符的“余数”与欧几里得“mod”进行比较。
当a 为负时,“欧几里得模”不同于C 的a%b 运算。
// a % b, the remainder after an integer division that truncates toward 0.
7 % 3 --> 1
7 % -3 --> 1
-7 % 3 --> -1
-7 % -3 --> -1
“Mod”或欧几里得除法中的模数。结果始终为 0 或正数。
7 modulo 3 --> 1
7 modulo -3 --> 1
-7 modulo 3 --> 2
-7 modulo -3 --> 2
候选模码:
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;
}
关于浮点数的注意事项:double fmod(double x, double y),虽然叫“fmod”,但与欧几里德除法“mod”不同,但类似于C整数余数:
fmod 函数计算x/y的浮点余数。 C11dr §7.12.10.1 2
fmod( 7, 3) --> 1.0
fmod( 7, -3) --> 1.0
fmod(-7, 3) --> -1.0
fmod(-7, -3) --> -1.0
Disambiguation:C 也有一个类似的命名函数double modf(double value, double *iptr),它将参数值分成整数和小数部分,每个部分都具有与参数相同的类型和符号。除了名称相似之外,这与这里的“mod”讨论几乎没有关系。
[编辑 2020 年 12 月]
对于那些在所有情况下都需要适当功能的人,改进的 modulo_Euclidean() 1) 检测到 mod(x,0) 和 2) modulo_Euclidean2(INT_MIN, -1) 的良好且没有 UB 结果。灵感来自4 different implementations of modulo with fully defined behavior。
int modulo_Euclidean2(int a, int b) {
if (b == 0) TBD_Code(); // perhaps return -1 to indicate failure?
if (b == -1) return 0; // This test needed to prevent UB of `INT_MIN % -1`.
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;
}
1 在 C99 之前,C 对 % 的定义仍然是除法的余数,但后来 / 允许向下舍入而不是“截断”趋向于零”。见Why do you get different values for integer division in C89?。因此,通过一些 C99 之前的编译,% 代码可以像欧几里得除法“mod”一样工作。上面的modulo_Euclidean() 也可以与这个备用的老式剩余部分一起使用。