如何确保模数的正值

时间:2017-07-02 19:23:25

标签: c

鉴于这个简单的随机生成器:

int i, r = 0;
for (i = 0; i < 50; i++) {
    r = (1234 * r + 101) % (11000000);
    printf("%d\n", r);
}

令人惊讶的是,我得到负值!

101
124735
10923091
192507
6553739
-7620565
-10842517
-10763989
-1860437
8188139

不应该是积极的价值观吗?有人可以解释一下吗?

3 个答案:

答案 0 :(得分:6)

您获得负值,因为您的程序具有整数算术溢出。对于签名类型int,实际上未定义该行为。您应该使用更大的类型来避免这种情况。类型unsigned long long保证至少有64个值位,这对于最大中间结果1234 * 10999999 + 101就足够了。

int i;
unsigned long long r = 0;
for (i = 0; i < 50; i++) {
    r = (1234 * r + 101) % 11000000;
    printf("%llu\n", r);
}

rici评论说r不需要是更大的类型,因为它的值在0..10999999范围内。这并不完全正确,因为类型int可能太小而无法处理此类值。 int的范围可以与-32767..32767一样小。

尽管如此,必须使用更大的类型执行中间计算,以避免算术溢出。这是相应的代码:

int i, r = 0;  // assuming 32-bit ints
for (i = 0; i < 50; i++) {
    r = (1234ULL * r + 101) % 11000000;
    printf("%d\n", r);
}

答案 1 :(得分:1)

正如您在其他答案中看到的,此行为是由于溢出造成的。

如果您希望之前能够检测到类似的内容,请使用gccclang的未定义行为消毒程序(UBSan)。

$ /opt/clang+llvm-4.0.0-armv7a-linux-gnueabihf/bin/clang -fsanitize=undefined don.c 

$ ./a.out 
don.c:8:18: runtime error: signed integer overflow: 1234 * 10923091 cannot be represented in type 'int'

don.c,第8行,第18列是此行中的乘法:r = (1234*r +101) % (11000000);

答案 2 :(得分:0)

你必须要小心,因为你的代码会产生溢出,即使你进行unsigned算术运算。

你的int变量可能是一个32位整数,它在数字2.147.483.647之后溢出,如果你考虑计算的最坏情况,你会得到1.234*10.999.999 + 101 ==> 13.573.998.867,然后计算模数运算,这会导致错误。

你可以做的最好的事情是使用64位数字进行这种计算而不是溢出,使用这个示例代码(即使是正常的结果,你也会看到不同的结果)

$ cat pru.c
#include <stdio.h>
#include <stdint.h>

int main()
{
    uint64_t i, r=0;
    for (i = 0; i < 50; i++) {
            r = (1234*r +101) % (11000000);
                printf("%llu\n", r);
    }
}

导致:

$ pru
101
124735
10923091
4094395
3483531
8677355
4856171
8515115
2652011
5581675
1787051
5221035
7757291
2497195
1538731
6794155
1987371
10415915
5239211
8186475
4110251
1049835
8496491
1669995
3773931
4030955
2198571
7036715
4306411
1111275
7313451
4798635
3515691
4362795
4689131
387755
5489771
9377515
10853611
6356075
396651
5467435
3814891
10575595
4284331
6864555
860971
6438315
2880811
1920875

这是正确的,因为1.234*10.999.999 + 101 ==> 13.573.998.867永远不会溢出uint64_t个数字(这是您可以获得的最大结果)并且会产生正确的结果。