[英]What, actually, is the modulo of a negative number?
我已经看到很多关于负数模的问题的答案。 每一个答案都设定了标准
(a/b)*b + a%b 等于 a
解释。 我可以使用这种方法计算任何模数,并且我理解有必要使用模函数,如果模数为负数才有意义,则将 b 添加到 a%b 的值。
我试图用外行的术语来理解这一点。 负数的模数是多少? 我在某处读到,您可以手动计算负数的正确模数,这是一些外行将数字加在一起的方法。 这会很有帮助,因为 a/b *b + a%b 方法有点乏味。
澄清一下,正数的模数可以用外行的术语解释为除数时的余数。 显然,在负数的情况下这不是真的,那么你如何正确地“理解”结果?
这曾经是在 C++ 的旧版本中实现定义的,但现在完全指定:
除法截断,即a / b
是丢弃小数部分的数学值。 例如, 9 / -5
是 -1.8 的截断,所以它是-1
。
余数运算a % b
由您提供的标识定义。 所以让我们计算: (a / b) * b
是-1 * -5
是5
,所以9 % -5
是4
。
相比之下, -9 % 5
是-4
。 因此,即使a / -b
与-a / b
相同,但a % -b
通常与-a % b
不同。 (类似地,模块化等价,其中两个整数是全等模的数学符号n
如果他们通过的整数倍不同n
,是不变的下更换n
与-n
)。
TL;DR:数学中使用的模运算符和 C++ %
运算符之间存在差异。
例如,让f(x) = x % 4
。 然后:
x : -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9
f(x) in math : 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1
f(x) in C : -1 -0 -3 -2 -1 0 -3 -2 -1 0 1 2 3 0 1 2 3 0 1
^~~~~~~~~~~~~~~~~~~~~~~~^
This part is different
您不需要任何特殊技巧来计算负数的 C++ 样式%
。
只需使用a%b == a - (a/b)*b
,它源自(a/b)*b + a%b == a
。
长答案:
Cppreference说如下:
二元运算符 % 产生第一个操作数除以第二个操作数的余数(在通常的算术转换之后;注意操作数类型必须是整数类型)。 如果商 a/b 可在结果类型中表示,则
(a/b)*b + a%b == a
。 如果第二个操作数为零,则行为未定义。 如果商 a/b 在结果类型中无法表示,则 a/b 和 a%b 的行为都未定义(这意味着 INT_MIN%-1 在 2 的补码系统上未定义)注意:在 C++11 之前,如果二元运算符 % 的一个或两个操作数为负,则余数的符号是实现定义的,因为它取决于整数除法的舍入方向。 在这种情况下,函数 std::div 提供了明确定义的行为。
重要的部分是:
(a/b)*b + a%b == a
。这意味着从 C++11 开始,运算符也为负操作数定义良好。
没有提到对负操作数的任何特殊处理,因此我们也可以说上面的身份词。
从(a/b)*b + a%b == a
我们可以很容易地推导出a%b
的公式:
a%b == a - (a/b)*b
如果你仔细想想,这个公式忽略的符号b
,作品仿佛模用的绝对值计算的a
,用的符号a
附加到该结果。
如果要计算“经典”模数,可以使用如下函数:
template <typename T, typename TT> constexpr T true_mod(T a, TT b)
{
static_assert(std::is_integral<T>::value &&
std::is_integral<TT>::value, "Argument types must be integral.");
if (a >= 0)
return a % b;
else
return (b >= 0 ? b : -b) - 1 + (a + 1) % b;
}
(a/b)*b + a%b 等于 a
即使此陈述为真,结果也可能从一种语言更改为另一种语言。
这种差异还取决于除法的结果。
例如,在 python 中,我有:
>>> # in python, use "//" for the floor division
>>> 3 // 4 # 0.75 is rounded to 0 : OK
0
>>> 3 % 4 # still as expected
3
>>> 0 * 4 + 3 # standard valided
3
>>> (-3) // 4 # -0.75 is rounded to -1, not 0 (the floor)
-1
>>> (-3) % 4 # the result is consistant; a modulo garanteed to be between 0 and b is very usefull
1
>>> (-1) * 4 + 1 # standard valided
-3
>>> 3 // (-4) # -0.75 is rounded to -1, not 0 (the floor)
-1
>>> 3 % (-4) # still a number between 0 and b
-1
>>> (-1) * (-4) + (-1) # standard valided
3
概括:
MODULO TEST: language=python
a=3 b=4 a/b=0 a%b=3 standard:true
a=-3 b=4 a/b=-1 a%b=1 standard:true
a=3 b=-4 a/b=-1 a%b=-1 standard:true
a=-3 b=-4 a/b=0 a%b=-3 standard:true
如果我的记忆力很好,即使标准有效,模数也不会像在 C 中那样工作。 这可能会非常令人不安。
我刚刚编写了一个小程序来测试 C 中的结果:
#include <stdio.h>
void test(int a, int b) {
int q = a/b;
int r = a%b;
int ok = q*b+r == a;
printf("a=%-2d b=%-2d a/b=%-2d a%%b=%-2d standard:%s\n", a, b, q, r, ok?"true":"false");
}
int main(int argc, char const *argv[]) {
printf("MODULO TEST: language=c\n");
test( 3, 4);
test(-3, 4);
test( 3,-4);
test(-3,-4);
return 0;
}
这使:
MODULO TEST: language=c
a=3 b=4 a/b=0 a%b=3 standard:true
a=-3 b=4 a/b=0 a%b=-3 standard:true
a=3 b=-4 a/b=0 a%b=3 standard:true
a=-3 b=-4 a/b=0 a%b=-3 standard:true
所以是的,该标准不足以为两个(负)数的模数确定一个独特的方法。
当左边的数字有未知符号时,您可以使用此代码:
int mod = a % b;
if (mod*b < 0) mod += b;
这段代码会一直给你一个 0 到b
之间的数字,就像在 python 中一样( 0 <= mod < b
,或者b < mod <= 0
如果 b 是负数)。
如果 b 是严格的正数(在大多数情况下),则* b
是无用的。
编辑
使用 XOR 比乘法更好,因为它可以防止溢出。
int mod = a % b;
if ((mod < 0) ^ (b < 0)) mod += b;
当 b 严格为正时:
int mod = a % b;
if (mod < 0) mod += b;
编辑 2 (2018-10-09)
更好地使用它,在 C 中使用 python 样式的除法(包含 0 和排除 b 之间的模):
int q = a / b;
int r = a % b;
if ((b<0) ? (r<0) : (r>0)) {
q -= 1;
r += b;
}
它可以防止“极端”情况,例如b
为负数并除以a
(例如6 % (-3)
)。 结果必须是0
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.