为什么rand()+ rand()产生负数?

我观察到rand()库函数在循环中只被调用一次,它几乎总是产生正数。

 for (i = 0; i < 100; i++) { printf("%d\n", rand()); } 

但是当我添加两个rand()调用时,生成的数字现在有更多的负数。

 for (i = 0; i < 100; i++) { printf("%d = %d\n", rand(), (rand() + rand())); } 

有人能解释为什么我在第二种情况下看到负数?

PS:我在循环之前初始化种子srand(time(NULL))

rand()定义为返回0RAND_MAX之间的整数。

 rand() + rand() 

可能溢出。 你观察到的可能是由整数溢出引起的未定义行为的结果。

问题是增加。 rand()返回一个int0...RAND_MAX 。 所以,如果你添加两个,你将达到RAND_MAX * 2 。 如果超过了INT_MAX ,则加法结果会溢出int可以保存的有效范围。 有符号值的溢出是未定义的行为,并可能导致您的键盘以外语方式与您交谈。

由于这里没有增加两个随机结果,所以简单的想法就是不去做。 或者,如果可以保存总和,则可以在添加前将每个结果转换为unsigned int 。 或者使用更大的types。 请注意, long不一定比int更宽,如果int至less为64位, int也是一样。

结论:只要避免添加。 它不提供更多的“随机性”。 如果您需要更多位,则可以连接值sum = a + b * (RAND_MAX + 1) ,但是这也可能需要比int更大的数据types。

正如你所说的原因是为了避免零结果:通过添加两个rand()调用的结果是无法避免的,因为两者都可以是零。 相反,你可以增加。 如果RAND_MAX == INT_MAX ,则不能在int完成。 但是, (unsigned int)rand() + 1会非常非常可能。 可能(而不是确定地),因为它确实需要UINT_MAX > INT_MAX ,我不确定是否有保证,但是对于我实际上知道的所有实现(而不仅仅是x86 / 64或ARM)有效。

警告:

虽然已经在这里注释了,但是请注意添加两个随机值并不是一个统一的分布,而是一个三angular形的分布,如滚动两个骰子:得到12 (两个骰子),两个骰子都必须显示6 。 对于11 ,已经有两种可能的变体: 6 + 55 + 6

所以,从这方面来说也是不好的。

还要注意, rand()生成的结果并不相互独立,因为它们是由伪随机数生成器生成的 。 还要注意,该标准没有规定计算值的质量或均匀分布。

这是澄清对这个答案发表评论的问题的答案 ,

我添加的原因是为了避免“0”作为我的代码中的随机数字。 兰特()+兰特()是快速肮脏的解决scheme,很容易出现在我的脑海里。

问题是避免0。提出的解决scheme存在(至less)两个问题。 正如其他答案指出的那样, rand()+rand()可以调用未定义的行为。 最好的build议是永远不要调用未定义的行为。 另一个问题是不能保证rand()不会连续两次产生0。

以下拒绝零,避免未定义的行为,在绝大多数情况下,将比两个调用rand()更快:

 int rnum; for (rnum = rand(); rnum == 0; rnum = rand()) {} // or do rnum = rand(); while (rnum == 0); 

基本上rand()产生0RAND_MAX之间的数字, 2 RAND_MAX > INT_MAX在你的情况下。

您可以使用数据types的最大值进行模数化以防止溢出。 这当然会扰乱随机数的分布,但rand只是一种获得快速随机数的方法。

 #include <stdio.h> #include <limits.h> int main(void) { int i=0; for (i=0; i<100; i++) printf(" %d : %d \n", rand(), ((rand() % (INT_MAX/2))+(rand() % (INT_MAX/2)))); for (i=0; i<100; i++) printf(" %d : %ld \n", rand(), ((rand() % (LONG_MAX/2))+(rand() % (LONG_MAX/2)))); return 0; } 

可能是你可以尝试一个相当棘手的方法,确保由2 rand()总和返回的值永远不会超过RAND_MAX的值。 一个可能的方法可能是sum = rand()/ 2 + rand()/ 2; 这将确保对于RAND_MAX值为32767的16位编译器,即使这两个rand恰好返回32767,即使(32767/2 = 16383)16383 + 16383 = 32766,也不会导致负和。

为了避免0,试试这个:

 int rnumb = rand()%(INT_MAX-1)+1; 

你需要包含limits.h。

对不起,我的英语不好。

我添加的原因是为了避免“0”作为我的代码中的随机数。 兰特()+兰特()是快速肮脏的解决scheme,很容易出现在我的脑海里。

一个简单的解决scheme(好吧,称之为“Hack”)永远不会产生零结果,永远不会溢出:

 x=(rand()/2)+1 // using divide -or- x=(rand()>>1)+1 // using shift which may be faster // compiler optimization may use shift in both cases 

这会限制你的最大值,但是如果你不关心这个,那么这对你来说应该是正常的。

尽pipe其他人都说过可能的溢出很可能是负面的原因,即使使用无符号整数。 真正的问题实际上是使用时间/datefunction作为种子。 如果你真的熟悉了这个function,你就会知道我为什么这么说。 正如它真正做的是给一个距离(经过的时间)从一个给定的date/时间。 虽然使用date/时间function作为rand()的种子是非常普遍的做法,但它并不是最好的select。 你应该寻找更好的select,因为关于这个主题有许多理论,我不可能进入所有的理论。 你在这个等式中增加了溢出的可能性,这种方法从一开始就注定了。

那些发布rand()+ 1的人正在使用最常用的解决scheme,以保证他们不会得到负数。 但是,这种方法也不是最好的方法。

你可以做的最好的事情是花费额外的时间来写和使用适当的exception处理,并且如果和/或当最终得到零结果时只添加到rand()数。 而且,正确地处理负数。 rand()函数并不完美,因此需要结合exception处理来使用,以确保您获得期望的结果。

花费额外的时间和精力去研究,学习和正确地实现rand()函数是非常值得花时间和精力的。 只是我的两分钱。 祝你好运