Skip to content

Commit e01ca02

Browse files
thejpsterjonathanpallant
authored andcommitted
Update random number generator
1 parent f243872 commit e01ca02

File tree

4 files changed

+64
-52
lines changed

4 files changed

+64
-52
lines changed

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ itoa = []
6161
memchr = []
6262
qsort = []
6363
rand_r = []
64-
rand = ["rand_r", "dep:portable-atomic"]
65-
rand-cs = ["rand", "portable-atomic/critical-section"]
64+
rand = ["rand_r"]
6665
signal = ["dep:portable-atomic"]
6766
signal-cs = ["portable-atomic/critical-section"]
6867
snprintf = []

examples/rand.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
fn main() {
2+
let mut distribution = std::collections::BTreeMap::new();
3+
4+
let mut seedp = 1;
5+
for _ in 0..4 {
6+
println!(".");
7+
for _ in 0..1_000_000 {
8+
let random = unsafe { tinyrlibc::rand_r(&mut seedp) };
9+
for place in 0..core::ffi::c_int::BITS {
10+
let random_bit = (random & (1 << place)) != 0;
11+
if random_bit {
12+
distribution
13+
.entry(place)
14+
.and_modify(|v| *v += 1)
15+
.or_insert(1);
16+
}
17+
}
18+
}
19+
}
20+
21+
for (k, v) in distribution.iter() {
22+
println!("{:02} => {}", k, v);
23+
}
24+
}

src/rand.rs

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use core::{
88

99
use portable_atomic::AtomicU32;
1010

11-
static RAND_STATE: AtomicU32 = AtomicU32::new(0x0);
11+
static RAND_STATE: AtomicU32 = AtomicU32::new(1);
1212

1313
/// Rust implementation of C library function `srand`
1414
#[cfg_attr(feature = "rand", no_mangle)]
@@ -17,43 +17,25 @@ pub extern "C" fn srand(seed: c_uint) {
1717
}
1818

1919
/// Rust implementation of C library function `rand`.
20-
///
21-
/// Returns a pseudo-random integer in the range 0 to [`RAND_MAX`](crate::RAND_MAX) (inclusive).
22-
/// This requires CAS operations. If your platform does not support them natively,
23-
/// you either have to enable the `rand-cs` feature of `tinyrlibc`,
24-
/// or the [`critical-section`](https://docs.rs/portable-atomic/1.9.0/portable_atomic/#optional-features-critical-section) feature,
25-
/// or the [`unsafe-assume-single-core`](https://docs.rs/portable-atomic/1.9.0/portable_atomic/#optional-features-unsafe-assume-single-core) feature
26-
/// in [`portable-atomic`](https://crates.io/crates/portable-atomic).
2720
#[cfg_attr(feature = "rand", no_mangle)]
2821
pub extern "C" fn rand() -> c_int {
29-
let mut current_state = RAND_STATE.load(Ordering::Relaxed);
30-
31-
loop {
32-
let mut new_state = current_state;
33-
let result = unsafe { crate::rand_r(&mut new_state as *mut _) };
34-
match RAND_STATE.compare_exchange_weak(
35-
current_state,
36-
new_state,
37-
Ordering::Relaxed,
38-
Ordering::Relaxed,
39-
) {
40-
Ok(_) => return result as _,
41-
Err(c) => current_state = c,
42-
}
43-
}
22+
let mut state = RAND_STATE.load(Ordering::Relaxed);
23+
let result = unsafe { crate::rand_r(&mut state) };
24+
RAND_STATE.store(state);
25+
result
4426
}
4527

4628
#[cfg(test)]
4729
mod test {
4830
use super::*;
4931
#[test]
5032
fn test_rand() {
51-
assert_eq!(rand(), 1012483);
52-
assert_eq!(rand(), 1716955678);
53-
assert_eq!(rand(), 1792309081);
33+
assert_eq!(rand(), 1012484);
34+
assert_eq!(rand(), 1716955679);
35+
assert_eq!(rand(), 1792309082);
5436
srand(5);
55-
assert_eq!(rand(), 234104183);
56-
assert_eq!(rand(), 1214203243);
57-
assert_eq!(rand(), 1803669307);
37+
assert_eq!(rand(), 234104184);
38+
assert_eq!(rand(), 1214203244);
39+
assert_eq!(rand(), 1803669308);
5840
}
5941
}

src/rand_r.rs

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,43 @@ use core::ffi::{c_int, c_uint};
55

66
#[cfg_attr(not(feature = "rand_r"), export_name = "tinyrlibc_RAND_MAX")]
77
#[cfg_attr(feature = "rand_r", no_mangle)]
8-
pub static RAND_MAX: c_int = 0x7FFF_FFFC as _;
8+
pub static RAND_MAX: c_int = c_int::MAX;
99

1010
/// Rust implementation of C library function `rand_r`
1111
///
1212
/// Passing NULL (core::ptr::null()) gives undefined behaviour.
1313
#[cfg_attr(not(feature = "rand_r"), export_name = "tinyrlibc_rand_r")]
1414
#[cfg_attr(feature = "rand_r", no_mangle)]
1515
pub unsafe extern "C" fn rand_r(seedp: *mut c_uint) -> c_int {
16-
// This algorithm is mentioned in the ISO C standard, here extended for 32 bits.
17-
let mut next = *seedp;
18-
let mut result: c_int;
19-
20-
next = next.wrapping_mul(1103515245);
21-
next = next.wrapping_add(12345);
22-
result = ((next / 65536) % 2048) as c_int;
16+
let mut result;
2317

24-
next = next.wrapping_mul(1103515245);
25-
next = next.wrapping_add(12345);
26-
result <<= 10;
27-
result ^= ((next / 65536) % 1024) as c_int;
18+
fn pump(input: c_uint) -> c_uint {
19+
// This algorithm is mentioned in the ISO C standard
20+
input.wrapping_mul(1103515245).wrapping_add(12345)
21+
}
2822

29-
next = next.wrapping_mul(1103515245);
30-
next = next.wrapping_add(12345);
31-
result <<= 10;
32-
result ^= ((next / 65536) % 1024) as c_int;
23+
fn select_top(state: c_uint, bits: usize) -> c_uint {
24+
// ignore the lower 16 bits, as they are low quality
25+
(state >> 16) & ((1 << bits) - 1)
26+
}
3327

28+
let mut next = *seedp;
29+
if c_int::MAX == 32767 {
30+
// pull 15 bits in one go
31+
next = pump(next);
32+
result = select_top(next, 15);
33+
} else {
34+
// pull 31 bits in three goes
35+
next = pump(next);
36+
result = select_top(next, 11) << 20;
37+
next = pump(next);
38+
result |= select_top(next, 10) << 10;
39+
next = pump(next);
40+
result |= select_top(next, 10);
41+
}
3442
*seedp = next;
3543

36-
result - 1
44+
result as c_int
3745
}
3846

3947
#[cfg(test)]
@@ -43,10 +51,9 @@ mod test {
4351
fn test_rand_r() {
4452
unsafe {
4553
let mut seed = 5;
46-
// Values taken from glibc implementation
47-
assert_eq!(rand_r(&mut seed), 234104183);
48-
assert_eq!(rand_r(&mut seed), 1214203243);
49-
assert_eq!(rand_r(&mut seed), 1803669307);
54+
assert_eq!(rand_r(&mut seed), 234104184);
55+
assert_eq!(rand_r(&mut seed), 1214203244);
56+
assert_eq!(rand_r(&mut seed), 1803669308);
5057
}
5158
}
5259
}

0 commit comments

Comments
 (0)