我需要运行可重复的蒙特卡罗模拟。这意味着我使用已知的种子并将其存储在我的结果中,如果我需要使用相同的随机数字运行相同的问题实例,我会使用该种子。这是常见的做法。
在研究数字精度的影响时,我遇到了以下问题:对于相同的Mersenne Twister种子,std::uniform_real_distribution<float>(-1, 1)
返回的数字与std::uniform_real_distribution<double>(-1, 1)
和std::uniform_real_distribution<long double>(-1, 1)
不同,如下面的示例所示:
#include <iomanip>
#include <iostream>
#include <random>
template < typename T >
void numbers( int seed ) {
std::mt19937 gen( seed );
std::uniform_real_distribution< T > dis( -1, 1 );
auto p = std::numeric_limits< T >::max_digits10;
std::cout << std::setprecision( p ) << std::scientific << std::setw( p + 7 )
<< dis( gen ) << "\n"
<< std::setw( p + 7 ) << dis( gen ) << "\n"
<< std::setw( p + 7 ) << dis( gen ) << "\n"
<< "**********\n";
}
int main() {
int seed = 123;
numbers< float >( seed );
numbers< double >( seed );
numbers< long double >( seed );
}
结果:
$ /usr/bin/clang++ -v
Apple LLVM version 10.0.0 (clang-1000.11.45.5)
Target: x86_64-apple-darwin18.2.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
$ /usr/bin/clang++ bug.cpp -std=c++17
$ ./a.out
3.929383755e-01
4.259105921e-01
-4.277213216e-01
**********
4.25910643160561708e-01
-1.43058149942132062e-01
3.81769702875451866e-01
**********
4.259106431605616525145e-01
-1.430581499421320209545e-01
3.817697028754518623166e-01
**********
如您所见,
double
和long double
从同一数字开始(除了精度的差异),并继续产生相同的值。另一方面,float
以完全不同的数字开始,其第二个数字类似于double
和long double
生成的第一个数字。您的编译器是否有相同的行为?这种意外的(对我来说)差异有原因吗?
方法
回答表明没有理由期望使用不同底层精度生成的值相同。
我将采取的方法是始终以最高可能的精度生成值,并根据需要将它们转换为较低的精度(例如,
float x = y
,其中y
是double
或long double
,视情况而定)。
float
和double
也是同样的情况。 - Max Langhoffloat
和double
之间的差异不仅在于精度(应用于浮点数类型的相同算法)。 - Escualo