I've just recently stumbled upon a C++ bug/feature, that I can't fully understand and was hoping someone with a better knowledge of C++ here could point me in the right direction.
Below you will find my attempt at finding out the area under the Gaussian curve using Monte Carlo integration. The recipe is:
- Generate a large number of normally distributed random variables (with mean of zero and standard deviation of one).
- Square these numbers.
- Take the average of all the squares. The average will be a very close estimation of the area under the curve (in the case of the Gaussian, it is 1.0).
The code below consists of two simple functions: rand_uni
, which returns a random variable, uniformly distributed between zero and one, and rand_norm
, which is a (rather poor, but 'good enough for government work') approximation of a normally distributed random variable.
main
runs through a loop one billion times, calling rand_norm
each time, squaring it with pow
and adding to the accumulating variable. After this loop the accumulated result is just divided by the number of runs and printed to the terminal as Result=<SOME NUMBER>
.
The problem lies in very quirky behaviour of the code below: when each generated random variable is printed to cout
(yes, one billion times), then the end result is correct regardless of the compiler used (1.0015, which is pretty close, to what I want). If I don't print the random variable in each loop iteration, I get inf
under gcc
and 448314 under clang
.
Frankly, this just boggles the mind and since it is my first(-ish) encounter with C++, I don't really know, what the problem might be: is it something with pow
? Does cout
act weird?
Any hint would be much appreciated!
The Source
// Monte Carlo integration of the Gaussian curve
#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;
enum {
no_of_runs = 1000000
};
// uniform random variable
double rand_uni() {
return ((double) rand() / (RAND_MAX));
};
// approximation of a normaly distributed random variable
double rand_norm() {
double result;
for(int i=12; i > 0; --i) {
result += rand_uni();
}
return result - 6;
};
int main(const int argc,
const char** argv) {
double result = 0;
double x;
for (long i=no_of_runs; i > 0; --i) {
x = pow(rand_norm(), 2);
#ifdef DO_THE_WEIRD_THING
cout << x << endl; // MAGIC?!
#endif
result += x;
}
// Prints the end result
cout << "Result="
<< result / no_of_runs
<< endl << endl;
}
The Makefile
CLANG=clang++
GCC=g++
OUT=normal_mc
default: *.cpp
$(CLANG) -o $(OUT).clang.a *.cpp
$(CLANG) -o $(OUT).clang.b -DDO_THE_WEIRD_THING *.cpp
$(GCC) -o $(OUT).gcc.a *.cpp
$(GCC) -o $(OUT).gcc.b -DDO_THE_WEIRD_THING *.cpp