/// <summary> /// Returns a random value sampled from the standard Gaussian distribution, i.e., with mean of 0 and standard deviation of 1. /// </summary> /// <param name="rng">Random source.</param> /// <returns>A new random sample.</returns> public static float Sample(IRandomSource rng) { for (;;) { // Generate 64 random bits. ulong u = rng.NextULong(); // Note. 32 random bits are required and therefore the lowest 32 bits are discarded // (a typical characteristic of PRNGs is that the least significant bits exhibit lower // quality randomness than the higher bits). // Select a segment (7 bits, bits 32 to 38). int s = (int)((u >> 32) & 0x7f); // Select the sign bit (bit 39), and convert to a single-precision float value of -1.0 or +1.0 accordingly. // Notes. // Here we convert the single chosen bit directly into IEEE754 single-precision floating-point format. // Previously this conversion used a branch, which is considerably slower because modern superscalar // CPUs rely heavily on branch prediction, but the outcome of this branch is pure random noise and thus // entirely unpredictable, i.e. the absolute worse case scenario! float sign = BitConverter.Int32BitsToSingle(unchecked ((int)(((u & 0x80_0000_0000UL) >> 8) | __oneBits))); // Get a uniform random value with interval [0, 2^24-1], or in hexadecimal [0, 0xff_ffff] // (i.e. a random 24 bit number) (bits 40 to 63). ulong u2 = u >> 40; // Special case for the base segment. if (s == 0) { if (u2 < __xComp[0]) { // Generated x is within R0. return(u2 * __INCR * __A_Div_Y0 * sign); } // Generated x is in the tail of the distribution. return(SampleTail(rng) * sign); } // All other segments. if (u2 < __xComp[s]) { // Generated x is within the rectangle. return(u2 * __INCR * __x[s] * sign); } // Generated x is outside of the rectangle. // Generate a random y coordinate and test if our (x,y) is within the distribution curve. // This execution path is relatively slow/expensive (makes a call to Math.Exp()) but is relatively rarely executed, // although more often than the 'tail' path (above). float x = u2 * __INCR * __x[s]; if (__y[s - 1] + ((__y[s] - __y[s - 1]) * rng.NextFloat()) < GaussianPdfDenormF(x)) { return(x * sign); } } }
/// <summary> /// Take a sample from the standard gaussian distribution, i.e. with mean of 0 and standard deviation of 1. /// </summary> public double SampleStandard() { for (;;) { // Generate 64 random bits. ulong u = _rng.NextULong(); // Notes. We require 61 of the random bits in total so we discard the lowest three bits because these // generally exhibit lower quality randomness than the higher bits (depending on the PRNG is use, but // it is a common feature of many PRNGs). // Select a segment (7 bits, bits 3 to 9). int s = (int)((u >> 3) & 0x7f); // Select sign bit (bit 10). double sign = ((u & 0x400) == 0) ? 1.0 : -1.0; // Get a uniform random value with interval [0, 2^53-1], or in hexadecimal [0, 0x1f_ffff_ffff_ffff] // (i.e. a random 53 bit number) (bits 11 to 63). ulong u2 = u >> 11; // Special case for the base segment. if (0 == s) { if (u2 < _xComp[0]) { // Generated x is within R0. return(u2 * __INCR * _A_Div_Y0 * sign); } // Generated x is in the tail of the distribution. return(SampleTail() * sign); } // All other segments. if (u2 < _xComp[s]) { // Generated x is within the rectangle. return(u2 * __INCR * _x[s] * sign); } // Generated x is outside of the rectangle. // Generate a random y coordinate and test if our (x,y) is within the distribution curve. // This execution path is relatively slow/expensive (makes a call to Math.Exp()) but is relatively rarely executed, // although more often than the 'tail' path (above). double x = u2 * __INCR * _x[s]; if (_y[s - 1] + ((_y[s] - _y[s - 1]) * _rng.NextDouble()) < GaussianPdfDenorm(x)) { return(x * sign); } } }
/// <summary> /// Take a sample from the standard Gaussian distribution, i.e. with mean of 0 and standard deviation of 1. /// </summary> /// <returns>A random sample.</returns> public static double Sample(this IRandomSource rng) { for (; ;) { // Generate 64 random bits. ulong u = rng.NextULong(); // Note. 61 random bits are required and therefore the lowest three bits are discarded // (a typical characteristic of PRNGs is that the least significant bits exhibit lower // quality randomness than the higher bits). // Select a segment (7 bits, bits 3 to 9). int s = (int)((u >> 3) & 0x7f); // Select sign bit (bit 10). double sign = ((u & 0x400) == 0) ? 1.0 : -1.0; // Get a uniform random value with interval [0, 2^53-1], or in hexadecimal [0, 0x1f_ffff_ffff_ffff] // (i.e. a random 53 bit number) (bits 11 to 63). ulong u2 = u >> 11; // Special case for the base segment. if (0 == s) { if (u2 < __xComp[0]) { // Generated x is within R0. return(u2 * __INCR * __A_Div_Y0 * sign); } // Generated x is in the tail of the distribution. return(SampleTail(rng) * sign); } // All other segments. if (u2 < __xComp[s]) { // Generated x is within the rectangle. return(u2 * __INCR * __x[s] * sign); } // Generated x is outside of the rectangle. // Generate a random y coordinate and test if our (x,y) is within the distribution curve. // This execution path is relatively slow/expensive (makes a call to Math.Exp()) but is relatively rarely executed, // although more often than the 'tail' path (above). double x = u2 * __INCR * __x[s]; if (__y[s - 1] + ((__y[s] - __y[s - 1]) * rng.NextDouble()) < GaussianPdfDenorm(x)) { return(x * sign); } } }