以下是从一个Opensource项目的rand()中复制的,它使用LCG
rand_next = rand_next * 1103515245L + 12345L; //unsigned long rand_next
经典的LCG是:
下一个=(下一个* a + c)mod M
显然,这里M是2 ^ 32。
让我困惑的是rand_next * 1103515245L,我很确定会发生溢出!我看看几个rand()实现,除了使用不同的a和c之外都采取这种方式。
那溢出有害吗?如果不是为什么?
由于
答案 0 :(得分:3)
这很好。根据C99规范,对于无符号长操作,the result is the same but reduced modulo 232 (§6.2.5):
A computation involving unsigned operands can never overflow, because a result
that cannot be represented by the resulting unsigned integer type is reduced
modulo the number that is one greater than the largest value that can be
represented by the resulting type.
所以这种行为实际上并不是“溢出”,但为了简单起见,我会称之为答案。因为模块化算术我们有
a1 ≡ b1 (mod m)
a2 ≡ b2 (mod m)
意味着
a1 + a2 ≡ b1 + b2 (mod m)
我们有
Next * a ≡ k (mod 2^32)
其中k
为Next * a
并带有“溢出”。因此,M = 2^32
,
Next * a + c ≡ k + c (mod M)
“溢出”的结果相当于模运算下没有“溢出”的结果,因此公式很好。一旦我们减少了模M = 2^32
,它就会得到相同的结果。
答案 1 :(得分:1)
您将signed long
与unsigned long
相乘。因此,*
的两个操作数具有相同的整数转换等级。在这种情况下,以下规则(C ++ 11,§5/ 9,第5项,第3项)适用:
[...]如果具有无符号整数类型的操作数的等级大于或等于 另一个操作数的类型的等级,带有符号整数类型的操作数应转换为带有无符号整数类型的操作数的类型。
因此,在计算乘法之前,两个操作数都会隐式转换为unsigned long
。因此,您得到无符号算术和无符号结果,并且相同的规则再次适用于加法运算。
unsigned的整数溢出是明确定义的(参见Zong Zhen Li的答案,刚刚扩展到详细介绍),所以没有问题。
关于C(而不是C ++),C11在§6.3.1.8/ 1中有相同的规则:
[...]如果具有无符号整数类型的操作数的等级大于或等于 等于另一个操作数的类型的等级,然后是操作数 有符号整数类型转换为带有unsigned的操作数的类型 整数类型。