HOWTO · C++
如何在 C++ 中生成一个随机浮点数
本文演示了如何在 C++ 中生成一个随机的浮点数。
本页内容
使用 C++11 的 <random> 库,可以在指定范围内生成伪随机浮点数。std::uniform_real_distribution 接受下限和上限,随机数引擎则提供数值序列。请根据应用所需的精度选择 float 或 double;只有在维护旧代码时才使用 rand。
使用 C++11 生成随机浮点数
这个完整程序会在半开区间 [10, 100) 内生成五个数。使用固定种子时,只要标准库实现相同,引擎生成的序列就可以重复,这在调试时很有帮助。由于标准没有规定分布算法必须生成可移植的序列,不同实现打印的具体数值可能不同。
运行此示例:
#include <iomanip>
#include <iostream>
#include <random>
int main() {
std::mt19937 engine(2026);
std::uniform_real_distribution<double> distribution(10.0, 100.0);
for (int i = 0; i < 5; ++i) {
std::cout << std::setprecision(10) << distribution(engine) << '\n';
}
}
78.99481399
96.50696843
96.65823206
73.63312607
11.77415091
分布模板参数决定结果类型。默认类型是 double;标准支持的类型有 float、double 和 long double。建议显式指定类型,让调用处的类型一目了然;示例使用 double,而 std::uniform_real_distribution<float> 会请求生成 float 值。上下限是实数,分布不包含上限。使用半开区间有助于缩放数值,避免恰好等于上限的值与下一个区间重叠。例如,范围 0.0 到 1.0 可表示归一化比例,范围 10.0 到 100.0 则可表示以所选单位计量的数值。
当 float 的精度足够,且需要节省存储空间或带宽时,可以选择 float;通常 double 在精度和取值范围之间更为均衡。如果确实需要更高精度,也可以使用 long double,但它能提供多少额外精度取决于具体实现。实际使用时,尽量让上下限的类型与分布类型一致,尤其是在更改模板参数时,这样更容易核对预期范围。即使引擎种子固定,分布也不保证不同标准库实现会生成相同的数值序列。
生成一系列数值时,应让引擎保持存活并重复使用。每次生成数值都重新创建并设定种子的引擎,可能会让序列重新开始或降低其质量。若需要可重复的结果,可像示例一样使用固定值作为种子。若希望每次运行得到不同的序列,在实现支持非确定性输出时,std::random_device 可以提供种子材料;但并非所有系统都保证这一行为。无论采用哪种方式,这种通用伪随机生成器都不适合生成安全敏感的数值。种子只在初始化引擎时使用一次,并不意味着每次抽取数值都要创建一个新的独立引擎。如果应用需要在多次运行间重现结果,请将种子与其他输入一并记录。若需要生成安全敏感的令牌,请使用密码学随机源,而不要使用此分布和引擎。
将 rand 用作有限的旧式替代方案
C 标准库中的 rand() 会返回一个从 0 到依赖具体实现的 RAND_MAX(含)之间的伪随机整数。要将其映射到浮点数范围,应先用浮点运算除以 RAND_MAX,再进行缩放。此示例映射到 [10, 100];当 rand() 返回 RAND_MAX 时,结果会包含上限:
#include <cstdlib>
#include <iostream>
int main() {
const double lower = 10.0;
const double upper = 100.0;
std::srand(2026);
for (int i = 0; i < 5; ++i) {
const double value = lower + (upper - lower) *
(static_cast<double>(std::rand()) / RAND_MAX);
std::cout << value << '\n';
}
}
60.2772
88.4741
67.8989
11.5099
83.7273
rand() 的内部状态和统计质量都有限;使用固定种子时,此示例在同一 C 库实现下可以重复生成结果,但这并不意味着结果不可预测或安全。编写新的 C++ 代码时,优先使用 <random>,因为它将引擎与分布分开,并允许直接选择结果类型和范围。