Search code examples
c++visual-studiorandomtoolchainmersenne-twister

Visual Studio C++ 2012 & 2017 show different behaviour in random number generation?


I am sampling random numbers from normal distribution using the following code snippet under both Visual Studio 2012 (TC: 11.0) and Visual Studio 2017 (TC: 14.1):

#include "stdafx.h"

#include <iostream>
#include <random>
#include <cmath>

int main()
{
    std::mt19937 e1;
    std::mt19937 e2;
    std::normal_distribution<> normal_dist(0, 1);

    for (int n = 0; n<10; ++n) {
        std::cout << "seq=" << e1();
        std::cout << "; PRN=" << normal_dist(e2);
        std::cout << std::endl;
    }

    return 0;
}

Under VS12 (TC: 11.0) I receive the following results:

seq=3499211612; PRN=0.253161
seq=581869302; PRN=-0.293219
seq=3890346734; PRN=0.0845901
seq=3586334585; PRN=-0.0570855
seq=545404204; PRN=0.992328
seq=4161255391; PRN=-1.43822
seq=3922919429; PRN=-0.910655
seq=949333985; PRN=0.106847
seq=2715962298; PRN=-0.600247
seq=1323567403; PRN=-0.844453

Likewise, under VS17 (TC: 14.1) I get

seq=3499211612; PRN=-0.146382
seq=581869302; PRN=0.13453
seq=3890346734; PRN=-1.87138
seq=3586334585; PRN=0.46065
seq=545404204; PRN=-0.214253
seq=4161255391; PRN=0.163712
seq=3922919429; PRN=-0.827944
seq=949333985; PRN=0.298595
seq=2715962298; PRN=1.05547
seq=1323567403; PRN=0.0102154

As I understand the sequence generation is tool chain independent, whereas the normal distribution sampling is not. What is the reason for this tool chain-dependency?

Many thanks in advance!


Solution

  • Apparently Microsoft changed some implementations in the random include. When drawing random numbers from a distribution at some point a uniform distribution sample is required. Under toolchain 14.1 the code for this task is:

    _NRAND(_Eng, _Ty)
    

    As I discovered, toolchains 11.0 of Visual Studio 2012 and 14.1 of Visual Studio 2017 exhibit different implementations for the macro _NRAND. The purpose of this macro is to map the sequence of integer values drawn from the PRN engine _Eng onto the interval [0, 1). The implementational difference is as follows:

    • Under Visual Studio 2012 each value of the sequence is rescaled in a straight forward fashion by substracting the sequence's minimum value and dividing by the difference of its maximum and minimum value.
    • Under Visual Studio 2017 _NRAND calls the function generate_canonical. For details on the implementation see line 282 ff in random. What it actually does is to calculate some kind of polynomial average on multiple rescaled elements of the sequence.