HOWTO · C++
C++ でランダムな浮動小数点数を生成する方法
この記事では、C++ で浮動小数点数の乱数を生成する方法を示します。
このページの内容
C++11 の <random> ライブラリを使うと、指定した範囲内の疑似乱数の浮動小数点値を生成できます。std::uniform_real_distribution に下限と上限を渡し、乱数エンジンから数列を取得します。必要な精度に応じて float または double を選びます。rand は既存のレガシーコードを保守する場合に限って使いましょう。
C++11 の でランダムな浮動小数点値を生成する
次の完全なプログラムは、半開区間 [10, 100) から値を5つ生成します。固定シードを使うと、同じ標準ライブラリ実装では乱数エンジンの数列が再現されるため、デバッグに便利です。ただし、分布アルゴリズムは移植可能な数列を生成するよう規定されていないため、表示される値は実装によって異なることがあります。
次の例を実行します。
#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 を選びます。精度と表現範囲の一般的なバランスを求める場合は double が適しています。追加の精度が必要なら long double を使えますが、どの程度精度が増すかは実装によって異なります。特にテンプレート引数を変更するときは、意図した範囲が分かりやすいよう、可能な限り上下限の型を分布の型にそろえてください。エンジンのシードを固定しても、標準ライブラリ実装が異なれば、分布が同じ数列を出力する保証はありません。
数列を生成するときは、エンジンを保持して使い回してください。値を生成するたびに新しいエンジンを作ってシードを設定すると、数列が繰り返されたり、品質が低下したりすることがあります。結果を再現したい場合は、上の例のように固定値をシードに使います。実行ごとに異なる数列にしたい場合、実装が非決定的な出力をサポートしていれば、std::random_device からシード用の値を取得できます。ただし、すべてのシステムでその動作が保証されるわけではありません。どちらの方法も、この汎用疑似乱数生成器をセキュリティ上重要な値に適したものにはしません。シードはエンジンを一度初期化するものであり、値を出すたびに独立したエンジンを作る指示ではありません。実行間で結果を再現する必要があるアプリケーションでは、ほかの入力とともにシードを記録してください。セキュリティ上重要なトークンが必要な場合は、この分布とエンジンではなく、暗号学的な乱数源を使います。
rand を限定的なレガシー代替として使う
C ライブラリの rand() は、実装依存の RAND_MAX 以下で 0 以上の疑似乱数整数を返します。浮動小数点の範囲に変換するには、スケーリングする前に浮動小数点演算で 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> を優先してください。エンジンと分布を分けて扱え、結果の型と範囲を直接選べます。