Skip to content

Commit

Permalink
Merge pull request #273 from dhardy/next-float
Browse files Browse the repository at this point in the history
Small changes from experimental branch
  • Loading branch information
dhardy committed Feb 28, 2018
2 parents ca3282a + 3966631 commit 3402373
Show file tree
Hide file tree
Showing 13 changed files with 405 additions and 357 deletions.
1 change: 0 additions & 1 deletion src/distributions/exponential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ mod test {
let mut rng = ::test::rng(221);
for _ in 0..1000 {
assert!(exp.sample(&mut rng) >= 0.0);
assert!(exp.sample(&mut rng) >= 0.0);
}
}
#[test]
Expand Down
66 changes: 49 additions & 17 deletions src/distributions/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

//! Basic floating-point number distributions

use core::mem;
use Rng;
use distributions::{Distribution, Uniform};


/// A distribution to sample floating point numbers uniformly in the open
/// interval `(0, 1)` (not including either endpoint).
Expand Down Expand Up @@ -52,48 +56,76 @@ pub struct Open01;
pub struct Closed01;


// Return the next random f32 selected from the half-open
// interval `[0, 1)`.
//
// This uses a technique described by Saito and Matsumoto at
// MCQMC'08. Given that the IEEE floating point numbers are
// uniformly distributed over [1,2), we generate a number in
// this range and then offset it onto the range [0,1). Our
// choice of bits (masking v. shifting) is arbitrary and
// should be immaterial for high quality generators. For low
// quality generators (ex. LCG), prefer bitshifting due to
// correlation between sequential low order bits.
//
// See:
// A PRNG specialized in double precision floating point numbers using
// an affine transition
//
// * <http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/dSFMT.pdf>
// * <http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/dSFMT-slide-e.pdf>
impl Distribution<f32> for Uniform {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f32 {
const UPPER_MASK: u32 = 0x3F800000;
const LOWER_MASK: u32 = 0x7FFFFF;
let tmp = UPPER_MASK | (rng.next_u32() & LOWER_MASK);
let result: f32 = unsafe { mem::transmute(tmp) };
result - 1.0
}
}
impl Distribution<f64> for Uniform {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
const UPPER_MASK: u64 = 0x3FF0000000000000;
const LOWER_MASK: u64 = 0xFFFFFFFFFFFFF;
let tmp = UPPER_MASK | (rng.next_u64() & LOWER_MASK);
let result: f64 = unsafe { mem::transmute(tmp) };
result - 1.0
}
}

macro_rules! float_impls {
($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => {
($mod_name:ident, $ty:ty, $mantissa_bits:expr) => {
mod $mod_name {
use Rng;
use distributions::{Distribution, Uniform};
use distributions::{Distribution};
use super::{Open01, Closed01};

const SCALE: $ty = (1u64 << $mantissa_bits) as $ty;

impl Distribution<$ty> for Uniform {
/// Generate a floating point number in the half-open
/// interval `[0,1)`.
///
/// See `Closed01` for the closed interval `[0,1]`,
/// and `Open01` for the open interval `(0,1)`.
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
rng.$method_name()
}
}
impl Distribution<$ty> for Open01 {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
// add 0.5 * epsilon, so that smallest number is
// greater than 0, and largest number is still
// less than 1, specifically 1 - 0.5 * epsilon.
rng.$method_name() + 0.5 / SCALE
let x: $ty = rng.gen();
x + 0.5 / SCALE
}
}
impl Distribution<$ty> for Closed01 {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
// rescale so that 1.0 - epsilon becomes 1.0
// precisely.
rng.$method_name() * SCALE / (SCALE - 1.0)
let x: $ty = rng.gen();
x * SCALE / (SCALE - 1.0)
}
}
}
}
}
float_impls! { f64_rand_impls, f64, 52, next_f64 }
float_impls! { f32_rand_impls, f32, 23, next_f32 }
float_impls! { f64_rand_impls, f64, 52 }
float_impls! { f32_rand_impls, f32, 23 }


#[cfg(test)]
Expand Down
5 changes: 0 additions & 5 deletions src/distributions/gamma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,6 @@ mod test {
let mut rng = ::test::rng(201);
for _ in 0..1000 {
chi.sample(&mut rng);
chi.sample(&mut rng);
}
}
#[test]
Expand All @@ -327,7 +326,6 @@ mod test {
let mut rng = ::test::rng(202);
for _ in 0..1000 {
chi.sample(&mut rng);
chi.sample(&mut rng);
}
}
#[test]
Expand All @@ -336,7 +334,6 @@ mod test {
let mut rng = ::test::rng(203);
for _ in 0..1000 {
chi.sample(&mut rng);
chi.sample(&mut rng);
}
}
#[test]
Expand All @@ -351,7 +348,6 @@ mod test {
let mut rng = ::test::rng(204);
for _ in 0..1000 {
f.sample(&mut rng);
f.sample(&mut rng);
}
}

Expand All @@ -361,7 +357,6 @@ mod test {
let mut rng = ::test::rng(205);
for _ in 0..1000 {
t.sample(&mut rng);
t.sample(&mut rng);
}
}
}
28 changes: 28 additions & 0 deletions src/distributions/integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,31 @@ impl Distribution<u128> for Uniform {
((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128)
}
}


#[cfg(test)]
mod tests {
use Rng;
use distributions::{Uniform};

#[test]
fn test_integers() {
let mut rng = ::test::rng(806);

rng.sample::<isize, _>(Uniform);
rng.sample::<i8, _>(Uniform);
rng.sample::<i16, _>(Uniform);
rng.sample::<i32, _>(Uniform);
rng.sample::<i64, _>(Uniform);
#[cfg(feature = "i128_support")]
rng.sample::<i128, _>(Uniform);

rng.sample::<usize, _>(Uniform);
rng.sample::<u8, _>(Uniform);
rng.sample::<u16, _>(Uniform);
rng.sample::<u32, _>(Uniform);
rng.sample::<u64, _>(Uniform);
#[cfg(feature = "i128_support")]
rng.sample::<u128, _>(Uniform);
}
}
7 changes: 0 additions & 7 deletions src/distributions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,13 +352,6 @@ fn ziggurat<R: Rng + ?Sized, P, Z>(
// creating a f64), so we might as well reuse some to save
// generating a whole extra random number. (Seems to be 15%
// faster.)
//
// This unfortunately misses out on the benefits of direct
// floating point generation if an RNG like dSMFT is
// used. (That is, such RNGs create floats directly, highly
// efficiently and overload next_f32/f64, so by not calling it
// this may be slower than it would be otherwise.)
// FIXME: investigate/optimise for the above.
let bits: u64 = rng.gen();
let i = (bits & 0xff) as usize;
let f = (bits >> 11) as f64 / SCALE;
Expand Down
2 changes: 0 additions & 2 deletions src/distributions/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ mod tests {
let mut rng = ::test::rng(210);
for _ in 0..1000 {
norm.sample(&mut rng);
norm.sample(&mut rng);
}
}
#[test]
Expand All @@ -183,7 +182,6 @@ mod tests {
let mut rng = ::test::rng(211);
for _ in 0..1000 {
lnorm.sample(&mut rng);
lnorm.sample(&mut rng);
}
}
#[test]
Expand Down
14 changes: 14 additions & 0 deletions src/distributions/other.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,17 @@ impl<T> Distribution<Option<T>> for Uniform where Uniform: Distribution<T> {
}
}
}


#[cfg(test)]
mod tests {
use {Rng, RngCore, Uniform};

#[test]
fn test_misc() {
let mut rng: &mut RngCore = &mut ::test::rng(820);

rng.sample::<char, _>(Uniform);
rng.sample::<bool, _>(Uniform);
}
}
141 changes: 141 additions & 0 deletions src/entropy_rng.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Entropy generator, or wrapper around external generators

use {RngCore, Error, impls};
use {OsRng, JitterRng};

/// An RNG provided specifically for seeding PRNGs.
///
/// Where possible, `EntropyRng` retrieves random data from the operating
/// system's interface for random numbers ([`OsRng`]); if that fails it will
/// fall back to the [`JitterRng`] entropy collector. In the latter case it will
/// still try to use [`OsRng`] on the next usage.
///
/// This is either a little slow ([`OsRng`] requires a system call) or extremely
/// slow ([`JitterRng`] must use significant CPU time to generate sufficient
/// jitter). It is recommended to only use `EntropyRng` to seed a PRNG (as in
/// [`thread_rng`]) or to generate a small key.
///
/// [`OsRng`]: os/struct.OsRng.html
/// [`JitterRng`]: jitter/struct.JitterRng.html
/// [`thread_rng`]: fn.thread_rng.html
#[derive(Debug)]
pub struct EntropyRng {
rng: EntropySource,
}

#[derive(Debug)]
enum EntropySource {
Os(OsRng),
Jitter(JitterRng),
None,
}

impl EntropyRng {
/// Create a new `EntropyRng`.
///
/// This method will do no system calls or other initialization routines,
/// those are done on first use. This is done to make `new` infallible,
/// and `try_fill_bytes` the only place to report errors.
pub fn new() -> Self {
EntropyRng { rng: EntropySource::None }
}
}

impl RngCore for EntropyRng {
fn next_u32(&mut self) -> u32 {
impls::next_u32_via_fill(self)
}

fn next_u64(&mut self) -> u64 {
impls::next_u64_via_fill(self)
}

fn fill_bytes(&mut self, dest: &mut [u8]) {
self.try_fill_bytes(dest).unwrap_or_else(|err|
panic!("all entropy sources failed; first error: {}", err))
}

fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
fn try_os_new(dest: &mut [u8]) -> Result<OsRng, Error>
{
let mut rng = OsRng::new()?;
rng.try_fill_bytes(dest)?;
Ok(rng)
}

fn try_jitter_new(dest: &mut [u8]) -> Result<JitterRng, Error>
{
let mut rng = JitterRng::new()?;
rng.try_fill_bytes(dest)?;
Ok(rng)
}

let mut switch_rng = None;
match self.rng {
EntropySource::None => {
let os_rng_result = try_os_new(dest);
match os_rng_result {
Ok(os_rng) => {
debug!("EntropyRng: using OsRng");
switch_rng = Some(EntropySource::Os(os_rng));
}
Err(os_rng_error) => {
warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}",
os_rng_error);
match try_jitter_new(dest) {
Ok(jitter_rng) => {
debug!("EntropyRng: using JitterRng");
switch_rng = Some(EntropySource::Jitter(jitter_rng));
}
Err(_jitter_error) => {
warn!("EntropyRng: JitterRng failed: {}",
_jitter_error);
return Err(os_rng_error);
}
}
}
}
}
EntropySource::Os(ref mut rng) => {
let os_rng_result = rng.try_fill_bytes(dest);
if let Err(os_rng_error) = os_rng_result {
warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}",
os_rng_error);
match try_jitter_new(dest) {
Ok(jitter_rng) => {
debug!("EntropyRng: using JitterRng");
switch_rng = Some(EntropySource::Jitter(jitter_rng));
}
Err(_jitter_error) => {
warn!("EntropyRng: JitterRng failed: {}",
_jitter_error);
return Err(os_rng_error);
}
}
}
}
EntropySource::Jitter(ref mut rng) => {
if let Ok(os_rng) = try_os_new(dest) {
debug!("EntropyRng: using OsRng");
switch_rng = Some(EntropySource::Os(os_rng));
} else {
return rng.try_fill_bytes(dest); // use JitterRng
}
}
}
if let Some(rng) = switch_rng {
self.rng = rng;
}
Ok(())
}
}
4 changes: 3 additions & 1 deletion src/jitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

//! Non-physical true random number generator based on timing jitter.

use {RngCore, Error, ErrorKind, impls};
use {RngCore, CryptoRng, Error, ErrorKind, impls};

use core::{fmt, mem, ptr};
#[cfg(feature="std")]
Expand Down Expand Up @@ -776,5 +776,7 @@ impl RngCore for JitterRng {
}
}

impl CryptoRng for JitterRng {}

// There are no tests included because (1) this is an "external" RNG, so output
// is not reproducible and (2) `test_timer` *will* fail on some platforms.
Loading

0 comments on commit 3402373

Please sign in to comment.