繁体   English   中英

实际上,负数的模是什么?

[英]What, actually, is the modulo of a negative number?

我已经看到很多关于负数模的问题的答案。 每一个答案都设定了标准

(a/b)*b + a%b 等于 a

解释。 我可以使用这种方法计算任何模数,并且我理解有必要使用模函数,如果模数为负数才有意义,则将 b 添加到 a%b 的值。

我试图用外行的术语来理解这一点。 负数的模数是多少? 我在某处读到,您可以手动计算负数的正确模数,这是一些外行将数字加在一起的方法。 这会很有帮助,因为 a/b *b + a%b 方法有点乏味。

澄清一下,正数的模数可以用外行的术语解释为除数时的余数。 显然,在负数的情况下这不是真的,那么你如何正确地“理解”结果?

这曾经是在 C++ 的旧版本中实现定义的,但现在完全指定:

  1. 除法截断,即a / b是丢弃小数部分的数学值。 例如, 9 / -5是 -1.8 的截断,所以它是-1

  2. 余数运算a % b由您提供的标识定义。 所以让我们计算: (a / b) * b-1 * -55 ,所以9 % -54

相比之下, -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 之前,如果二元运算符 % 的一个或两个操作数为负,则余数的符号是​​实现定义的。”

这意味着从 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM