0

I have a problem where I really need to seed a random number generator with a double value. The generated random number should be between {min, max}. My current solution is

double map(double in, double min, double max){
    size_t seed = std::hash<double>()(in);
    unsigned int seed2 = (unsigned int)(seed >> (sizeof(size_t)-sizeof(unsigned int)));
    std::mt19937 gen{seed2}; 
    std::uniform_real_distribution<double> dis(min, max);
    return dis(gen);
}

The problem with the above is that hash to size_t will not be the same on all platforms. Therefore expect the result will different on various platforms. Can this algorithm be altered to generate the same output for the same input on all platforms whilst still being pseudorandom.

Example at https://godbolt.org/z/rfT3ajbbo

On 64-bit gcc build the output is

0.9223
0.0733052
0.0168922
0.993303
0.330736

on 32-bit gcc build the output is

0.121904
0.188877
0.839134
0.984475
0.171036
phuclv
  • 37,963
  • 15
  • 156
  • 475
bradgonesurfing
  • 30,949
  • 17
  • 114
  • 217
  • 3
    There is no guarantee that `uniform_real_distribution` on different implementations will produce the same results anyway. Size of `std::size_t` seems like a secondary issue to that. – user17732522 Aug 05 '22 at 08:24
  • 1
    And neither is `std::hash`. – user17732522 Aug 05 '22 at 08:26
  • I'm happy to ditch the entire implementation. The point remains. Is it possible to write a pseudo random generator using a double seed that produces the same sequence across platforms? The implementation is just an example of the behaviour I want. – bradgonesurfing Aug 05 '22 at 08:26
  • Does this answer your question? [Non-reproducible random numbers using \`\`](https://stackoverflow.com/questions/45936816/non-reproducible-random-numbers-using-random) – Peter O. Aug 05 '22 at 08:28
  • Note this is not for crypto purposes. It is for randomizing some geometric data but our testfiles need to pass on all our build platforms. – bradgonesurfing Aug 05 '22 at 08:28
  • Not with ``'s distributions. You will at least have to write the distribution generating the `double` value from the mersenne twister output yourself or use a different library. – user17732522 Aug 05 '22 at 08:28
  • @PeterO. https://stackoverflow.com/a/62735164/158285 this does have useful information. I think I need to write my own *standard* algorithm so I don't rely on the std library. – bradgonesurfing Aug 05 '22 at 08:30
  • @bradgonesurfing : Or just use Boost.Random – ildjarn Aug 05 '22 at 10:16

1 Answers1

1

Why do you need to hash double? You can just use its binary pattern as the seed

double map(double in, double min, double max){
    auto seed = std::bit_cast<uint64_t>(in);
    auto seed2 = uint32_t((seed >> 32) | seed);
    std::mt19937 gen{seed2};
    std::uniform_real_distribution<double> dis(min, max);
    return dis(gen);
}

double map2(double in, double min, double max){
    auto seed = std::bit_cast<uint64_t>(in);
    std::mt19937_64 gen{seed};
    std::uniform_real_distribution<double> dis(min, max);
    return dis(gen);
}

Demo on Godbolt

phuclv
  • 37,963
  • 15
  • 156
  • 475
  • I don't have bitcast yet. Still on c+17 but yes you are right. My actual solution used memcpy but yours is better. However std::uniform_real_distribution is not guaranteed to be reproducible between compilers ‍♀️ which seems to be an oversight of the standard. I ended up writing my own version which I control and it is good enough for the limited purposes I have. – bradgonesurfing Aug 05 '22 at 15:35
  • 1
    @bradgonesurfing you just need `uint64_t seed; memcpy(&seed, &in, sizeof seed);`, it's the standard way for type punning in C++ pre-C++20. I use `bit_cast` just to avoid wasting a line for that – phuclv Aug 05 '22 at 15:37
  • @bradgonesurfing I don't think that's true. The C++ standard already specifies [fixed constants for `mt19937` and `mt19937_64`](https://en.cppreference.com/w/cpp/numeric/random/mersenne_twister_engine) so the output should be consistent across platforms. You can check the Godbolt link in my answer, the outputs for Windows and Linux are the same – phuclv Aug 05 '22 at 15:48
  • 1
    ok I've checked and the issue is `std::uniform_real_distribution`, not `std::mt19937`. [This question](https://stackoverflow.com/q/42475773/995714) claims that `std::uniform_real_distribution` is consistent between Windows and Mac but probably still not as reliable – phuclv Aug 05 '22 at 16:04