std::normal_distribution<double> 导致 Windows 与 Linux 的顺序错误?

std::normal_distribution<double> results in wrong order windows versus linux?

本文关键字:Linux 顺序 错误 Windows double normal distribution lt gt std 导致      更新时间:2023-10-16

有人访问过这个问题吗?每个实现都不需要产生相同的数据。在实践中——在arm、x86、免费和商业编译器之间,STL实现有很多不同吗?

// g++ --std=c++11 -o a minimal.cpp && ./a
#include <iostream>
#include <random>
using namespace std;
int
main()
{
  std::mt19937_64 gen;
  gen.seed(17);
  cout << "nNormaln";
  normal_distribution<double> distr1;
  for (int i = 0; i < 2; i++) {
    double delay = distr1(gen);
    printf("  Value = %15.15gn", delay);
  }
  return(0);
}

/*
Results
(1) gcc-4.8.0 linux 64b version result
  Normal
    Value = 1.03167351251536
    Value = 1.21967569130525
(2) Microsoft Visual Studio Community 2015 Version 14.0.23107.0 D14REL
      or 
    Microsoft Visual Studio Professional 2012 Version 11.0.60610.01 Update 3
  Normal
    Value = 1.21967569130525
    Value = 1.03167351251536  // same values in wrong (different) order
*/

我可以理解在某些特殊的硬件平台上对生成器或分发使用不同的算法,但这种差异似乎更像是一个bug。

下面是我用来诊断差异来自哪里并解决它的一些代码:-生成器和统一分布匹配在win和linux上。-除成对顺序外,正态分布在数字上匹配

//   g++ --std=c++11 -o a workaround.cpp && ./a

#include <iostream>
#include <random>
#include <stack>

using namespace std;
typedef  std::mt19937_64  RandGenLowType;
// Helper wrapper - it did confirm that the differences
// do NOT come from the generator
class RandGenType : public RandGenLowType {
  public:
    result_type operator()() {
      result_type val = RandGenLowType::operator()();
      printf("    Gen pulled %20llun", val);
      return(val);
    }
};

typedef normal_distribution<double> NormalDistrLowType;
// Workaround wrapper to swap the output data stream pairwise
class NormalDistrType : NormalDistrLowType {
  public:
    result_type operator()(RandGenType &pGen) {
      // Keep single flow (used variables, includes) same for all platforms
      if (win64WaStack.empty()) {
        win64WaStack.push(NormalDistrLowType::operator()(pGen));
#ifdef _MSC_VER
        win64WaStack.push(NormalDistrLowType::operator()(pGen));
#endif
      }
      result_type lResult = win64WaStack.top();
      win64WaStack.pop();
      return(lResult);
    }    
  private:
    std::stack<result_type> win64WaStack;
};

int
main()
{
  RandGenType gen;
  gen.seed(17);
  // No platform issue, no workaround used
  cout << "nUniformn";
  uniform_real_distribution<double> distr;
  for (int i = 0; i < 4; i++) {
    double delay = distr(gen);
    printf("  Delay = %15.15gn", delay);
  }
  // Requires the workaround
#ifdef _MSC_VER
  cout << "Workaround code is active, swapping the output stream pairwisen";
#endif
  cout << "nNormaln";
  //normal_distribution<float> distr1;
  NormalDistrType distr1;
  for (int i = 0; i < 10; i++) {
    double delay = distr1(gen);
    printf("  Value = %15.15gn", delay);
  }
  return(0);
}

生成正态分布随机数的几种常用方法,如Box-Muller变换和Marsaglia极坐标法,一次生成两个随机数。使用这些方法之一的分布对象将生成两个随机数,返回其中一个,并将另一个保存到下次调用时使用。

返回哪一个,存储哪一个,当然完全取决于库的作者。看起来libstdc++和MSVC使用相同的算法,但碰巧选择不同。