为何在类型转换前对rand函数中的next变量执行除法运算?
我正在研究K&R2第2.7节类型转换中的伪随机数生成器(RNG):
unsigned long int next = 1; /* rand: 返回0..32767范围内的伪随机整数 */ int rand(void) { next = next * 1103515245 + 12345; return (unsigned int)(next / 65536) % 32768; } /* srand: 为rand()设置种子 */ void srand(unsigned int seed) { next = seed; }
为什么要在将next强制转换为unsigned int之前先除以65536?
我的第一反应是这与当时int为16位或RAND_MAX为32767有关,但在我当前int为32位、RAND_MAX为2147483647的机器上,得到的结果仍然一致。抱歉如果我忽略了某些明显的点,也抱歉给那些认为K&R2过时的人带来困扰——该示例仍出现在C标准C17 7.22.2.2 srand函数中,因此我认为我的疑问在如今依然成立。
核心原因:历史兼容性设计 + 跨平台序列一致性
16位int时代的必要性:K&R2问世时,主流系统的
int是16位(无符号范围065535,有符号范围-3276832767),而next是unsigned long类型(至少32位)。如果直接把32位的next转换为16位unsigned int,会直接截断高16位,丢失伪随机序列的有效高位信息。先除以65536(即2^16)相当于把next的高16位移到低16位的位置,此时转换为16位unsigned int就能完整保留这部分有效位,再通过%32768(2^15)截取低15位,得到0~32767范围内的结果,完美适配当时的RAND_MAX要求。现代平台的序列一致性:即使现在
int普遍是32位,这个操作依然能保证生成的伪随机序列和C标准定义的完全一致。对于32位的unsigned long来说,next / 65536等价于取高16位,转换为32位unsigned int时不会有任何截断,后续的%32768则是取这16位中的低15位,最终结果和直接操作位(比如(next >> 16) & 0x7FFF)完全相同,确保了跨平台的序列一致性——这也是C17标准依然保留该实现示例的原因。代码风格的考量:用除法
next / 65536而不是位操作next >> 16,在早期C标准中更直观地表达了“提取高16位”的意图,同时unsigned整数的除法定义为向零截断,和右移效果完全一致,兼顾了可读性和正确性。
内容的提问来源于stack exchange,提问作者rsarson

