VB6 的 Rnd 和 Randomize 使用什么伪随机数算法?
结论
VB6 的 Rnd 使用 24 位线性同余生成器(LCG)维护随机状态。每次生成时,状态按以下公式更新,再除以 2^24,得到 [0, 1) 范围内的 Single:
state = (state * 0x43FD43FD + 0x00C39EC3) & 0xFFFFFF
result = state / 16777216
.NET 的 Microsoft.VisualBasic.VBMath 源码将这段逻辑标注为与 VB6 的 rtRandomNext 等价。Randomize 会从定时器或数值参数中提取位模式,用来替换状态的中间两个字节;参数本身不会直接成为状态值。Microsoft.VisualBasic.VBMath 源码
Rnd 如何更新状态
无参数的 Rnd 可以理解为调用 rtcRandomNext(1.0)。参数决定如何处理状态:
- 参数为正数:推进当前随机序列一次。
- 参数为
0:不推进状态,只返回当前状态对应的数值。 - 参数为负数:先根据参数的
Single位模式构造新状态,再推进一次。
核心逻辑可用以下伪代码表示:
if number < 0:
bits = bitcast_to_uint32(number)
state = (bits + (bits >> 24)) & 0xFFFFFF
if number != 0:
state = (state * 0x43FD43FD + 0x00C39EC3) & 0xFFFFFF
return state / 16777216.0
这里的 bitcast_to_uint32 是按位解释 Single 的 32 位表示,不是将浮点数值转换为整数。要兼容旧程序,浮点精度、位转换和 24 位掩码都需要与 VB6 保持一致。
Randomize 如何处理种子
不带参数的 Randomize 使用系统定时器生成的 Single 值。带数值参数时,rtcRandomize 接收 Double。运行时从该值的位模式中取出相应的 32 位部分,将两个 16 位部分异或后左移 8 位,再与当前状态的首尾字节拼接。概念上可写成:
seed_bits = extract_32_bits(value)
middle = ((seed_bits & 0xFFFF) ^ (seed_bits >> 16)) << 8
state = (state & 0xFF0000FF) | middle
无参数形式会先把当前时间转换为 Single,再执行相同的拼接步骤。由于首尾字节会保留,即使连续两次使用相同的 Randomize 数值,也不一定会重放相同序列。相关位处理也见于 .NET 的兼容实现。VB6 Randomize 参考
迁移时的注意事项
要复现遗留程序的输出,需要同时复现 Randomize 的位拼接、Rnd 对负参数的处理,以及每次调用推进状态的顺序。仅使用相同的数值种子,或改用另一种语言的默认随机数生成器,都不能保证序列一致。以上公式描述了生成器的核心状态更新,运行时入口的精确行为还取决于参数类型和位级处理。
备注:内容仅供参考。
