Skip to content

Commit d80efb2

Browse files
committed
Implement Standard for SIMD float types
1 parent 38b9a29 commit d80efb2

File tree

1 file changed

+155
-43
lines changed

1 file changed

+155
-43
lines changed

src/distributions/float.rs

Lines changed: 155 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
use core::mem;
1414
use Rng;
1515
use distributions::{Distribution, Standard};
16+
#[cfg(feature="simd_support")]
17+
use core::simd::*;
1618

1719
/// A distribution to sample floating point numbers uniformly in the half-open
1820
/// interval `(0, 1]`, i.e. including 1 but not 0.
@@ -144,63 +146,173 @@ float_impls! { f32, u32, 23, 127 }
144146
float_impls! { f64, u64, 52, 1023 }
145147

146148

149+
#[cfg(feature="simd_support")]
150+
macro_rules! simd_float_impls {
151+
($ty:ident, $uty:ident, $f_scalar:ty, $u_scalar:ty,
152+
$fraction_bits:expr, $exponent_bias:expr) => {
153+
impl IntoFloat for $uty {
154+
type F = $ty;
155+
#[inline(always)]
156+
fn into_float_with_exponent(self, exponent: i32) -> $ty {
157+
// The exponent is encoded using an offset-binary representation
158+
let exponent_bits: $u_scalar =
159+
(($exponent_bias + exponent) as $u_scalar) << $fraction_bits;
160+
unsafe { mem::transmute(self | $uty::splat(exponent_bits)) }
161+
}
162+
}
163+
164+
impl Distribution<$ty> for Standard {
165+
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
166+
// Multiply-based method; 24/53 random bits; [0, 1) interval.
167+
// We use the most significant bits because for simple RNGs
168+
// those are usually more random.
169+
let float_size = mem::size_of::<$f_scalar>() * 8;
170+
let precision = $fraction_bits + 1;
171+
let scale = $ty::splat(1.0 / ((1 as $u_scalar << precision) as $f_scalar));
172+
173+
let value: $uty = rng.gen();
174+
let value = $ty::from(value >> (float_size - precision));
175+
scale * value
176+
}
177+
}
178+
179+
impl Distribution<$ty> for OpenClosed01 {
180+
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
181+
// Multiply-based method; 24/53 random bits; (0, 1] interval.
182+
// We use the most significant bits because for simple RNGs
183+
// those are usually more random.
184+
let float_size = mem::size_of::<$f_scalar>() * 8;
185+
let precision = $fraction_bits + 1;
186+
let scale = $ty::splat(1.0 / ((1 as $u_scalar << precision) as $f_scalar));
187+
188+
let value: $uty = rng.gen();
189+
// Add 1 to shift up; will not overflow because of right-shift:
190+
let value = $ty::from((value >> (float_size - precision)) + 1);
191+
scale * value
192+
}
193+
}
194+
195+
impl Distribution<$ty> for Open01 {
196+
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
197+
// Transmute-based method; 23/52 random bits; (0, 1) interval.
198+
// We use the most significant bits because for simple RNGs
199+
// those are usually more random.
200+
const EPSILON: $f_scalar = 1.0 / (1u64 << $fraction_bits) as $f_scalar;
201+
let float_size = mem::size_of::<$f_scalar>() * 8;
202+
203+
let value: $uty = rng.gen();
204+
let fraction = value >> (float_size - $fraction_bits);
205+
fraction.into_float_with_exponent(0) - $ty::splat(1.0 - EPSILON / 2.0)
206+
}
207+
}
208+
}
209+
}
210+
211+
#[cfg(feature="simd_support")]
212+
simd_float_impls! { f32x2, u32x2, f32, u32, 23, 127 }
213+
#[cfg(feature="simd_support")]
214+
simd_float_impls! { f32x4, u32x4, f32, u32, 23, 127 }
215+
#[cfg(feature="simd_support")]
216+
simd_float_impls! { f32x8, u32x8, f32, u32, 23, 127 }
217+
#[cfg(feature="simd_support")]
218+
simd_float_impls! { f32x16, u32x16, f32, u32, 23, 127 }
219+
220+
#[cfg(feature="simd_support")]
221+
simd_float_impls! { f64x2, u64x2, f64, u64, 52, 1023 }
222+
#[cfg(feature="simd_support")]
223+
simd_float_impls! { f64x4, u64x4, f64, u64, 52, 1023 }
224+
#[cfg(feature="simd_support")]
225+
simd_float_impls! { f64x8, u64x8, f64, u64, 52, 1023 }
226+
227+
147228
#[cfg(test)]
148229
mod tests {
149230
use Rng;
150231
use distributions::{Open01, OpenClosed01};
151232
use rngs::mock::StepRng;
233+
#[cfg(feature="simd_support")]
234+
use core::simd::*;
152235

153236
const EPSILON32: f32 = ::core::f32::EPSILON;
154237
const EPSILON64: f64 = ::core::f64::EPSILON;
155238

156-
#[test]
157-
fn standard_fp_edge_cases() {
158-
let mut zeros = StepRng::new(0, 0);
159-
assert_eq!(zeros.gen::<f32>(), 0.0);
160-
assert_eq!(zeros.gen::<f64>(), 0.0);
161-
162-
let mut one32 = StepRng::new(1 << 8, 0);
163-
assert_eq!(one32.gen::<f32>(), EPSILON32 / 2.0);
164-
165-
let mut one64 = StepRng::new(1 << 11, 0);
166-
assert_eq!(one64.gen::<f64>(), EPSILON64 / 2.0);
167-
168-
let mut max = StepRng::new(!0, 0);
169-
assert_eq!(max.gen::<f32>(), 1.0 - EPSILON32 / 2.0);
170-
assert_eq!(max.gen::<f64>(), 1.0 - EPSILON64 / 2.0);
171-
}
172-
173-
#[test]
174-
fn openclosed01_edge_cases() {
175-
let mut zeros = StepRng::new(0, 0);
176-
assert_eq!(zeros.sample::<f32, _>(OpenClosed01), 0.0 + EPSILON32 / 2.0);
177-
assert_eq!(zeros.sample::<f64, _>(OpenClosed01), 0.0 + EPSILON64 / 2.0);
178-
179-
let mut one32 = StepRng::new(1 << 8, 0);
180-
assert_eq!(one32.sample::<f32, _>(OpenClosed01), EPSILON32);
239+
macro_rules! test_f32 {
240+
($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => {
241+
#[test]
242+
fn $fnn() {
243+
// Standard
244+
let mut zeros = StepRng::new(0, 0);
245+
assert_eq!(zeros.gen::<$ty>(), $ZERO);
246+
let mut one = StepRng::new(1 << 8 | 1 << (8 + 32), 0);
247+
assert_eq!(one.gen::<$ty>(), $EPSILON / 2.0);
248+
let mut max = StepRng::new(!0, 0);
249+
assert_eq!(max.gen::<$ty>(), 1.0 - $EPSILON / 2.0);
181250

182-
let mut one64 = StepRng::new(1 << 11, 0);
183-
assert_eq!(one64.sample::<f64, _>(OpenClosed01), EPSILON64);
251+
// OpenClosed01
252+
let mut zeros = StepRng::new(0, 0);
253+
assert_eq!(zeros.sample::<$ty, _>(OpenClosed01),
254+
0.0 + $EPSILON / 2.0);
255+
let mut one = StepRng::new(1 << 8 | 1 << (8 + 32), 0);
256+
assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON);
257+
let mut max = StepRng::new(!0, 0);
258+
assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + 1.0);
184259

185-
let mut max = StepRng::new(!0, 0);
186-
assert_eq!(max.sample::<f32, _>(OpenClosed01), 1.0);
187-
assert_eq!(max.sample::<f64, _>(OpenClosed01), 1.0);
260+
// Open01
261+
let mut zeros = StepRng::new(0, 0);
262+
assert_eq!(zeros.sample::<$ty, _>(Open01), 0.0 + $EPSILON / 2.0);
263+
let mut one = StepRng::new(1 << 9 | 1 << (9 + 32), 0);
264+
assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / 2.0 * 3.0);
265+
let mut max = StepRng::new(!0, 0);
266+
assert_eq!(max.sample::<$ty, _>(Open01), 1.0 - $EPSILON / 2.0);
267+
}
268+
}
188269
}
270+
test_f32! { f32_edge_cases, f32, 0.0, ::core::f32::EPSILON }
271+
#[cfg(feature="simd_support")]
272+
test_f32! { f32x2_edge_cases, f32x2, f32x2::splat(0.0), f32x2::splat(EPSILON32) }
273+
#[cfg(feature="simd_support")]
274+
test_f32! { f32x4_edge_cases, f32x4, f32x4::splat(0.0), f32x4::splat(EPSILON32) }
275+
#[cfg(feature="simd_support")]
276+
test_f32! { f32x8_edge_cases, f32x8, f32x8::splat(0.0), f32x8::splat(EPSILON32) }
277+
#[cfg(feature="simd_support")]
278+
test_f32! { f32x16_edge_cases, f32x16, f32x16::splat(0.0), f32x16::splat(EPSILON32) }
189279

190-
#[test]
191-
fn open01_edge_cases() {
192-
let mut zeros = StepRng::new(0, 0);
193-
assert_eq!(zeros.sample::<f32, _>(Open01), 0.0 + EPSILON32 / 2.0);
194-
assert_eq!(zeros.sample::<f64, _>(Open01), 0.0 + EPSILON64 / 2.0);
280+
macro_rules! test_f64 {
281+
($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => {
282+
#[test]
283+
fn $fnn() {
284+
// Standard
285+
let mut zeros = StepRng::new(0, 0);
286+
assert_eq!(zeros.gen::<$ty>(), $ZERO);
287+
let mut one = StepRng::new(1 << 11, 0);
288+
assert_eq!(one.gen::<$ty>(), $EPSILON / 2.0);
289+
let mut max = StepRng::new(!0, 0);
290+
assert_eq!(max.gen::<$ty>(), 1.0 - $EPSILON / 2.0);
195291

196-
let mut one32 = StepRng::new(1 << 9, 0);
197-
assert_eq!(one32.sample::<f32, _>(Open01), EPSILON32 / 2.0 * 3.0);
292+
// OpenClosed01
293+
let mut zeros = StepRng::new(0, 0);
294+
assert_eq!(zeros.sample::<$ty, _>(OpenClosed01),
295+
0.0 + $EPSILON / 2.0);
296+
let mut one = StepRng::new(1 << 11, 0);
297+
assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON);
298+
let mut max = StepRng::new(!0, 0);
299+
assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + 1.0);
198300

199-
let mut one64 = StepRng::new(1 << 12, 0);
200-
assert_eq!(one64.sample::<f64, _>(Open01), EPSILON64 / 2.0 * 3.0);
201-
202-
let mut max = StepRng::new(!0, 0);
203-
assert_eq!(max.sample::<f32, _>(Open01), 1.0 - EPSILON32 / 2.0);
204-
assert_eq!(max.sample::<f64, _>(Open01), 1.0 - EPSILON64 / 2.0);
301+
// Open01
302+
let mut zeros = StepRng::new(0, 0);
303+
assert_eq!(zeros.sample::<$ty, _>(Open01), 0.0 + $EPSILON / 2.0);
304+
let mut one = StepRng::new(1 << 12, 0);
305+
assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / 2.0 * 3.0);
306+
let mut max = StepRng::new(!0, 0);
307+
assert_eq!(max.sample::<$ty, _>(Open01), 1.0 - $EPSILON / 2.0);
308+
}
309+
}
205310
}
311+
test_f64! { f64_edge_cases, f64, 0.0, EPSILON64 }
312+
#[cfg(feature="simd_support")]
313+
test_f64! { f64x2_edge_cases, f64x2, f64x2::splat(0.0), f64x2::splat(EPSILON64) }
314+
#[cfg(feature="simd_support")]
315+
test_f64! { f64x4_edge_cases, f64x4, f64x4::splat(0.0), f64x4::splat(EPSILON64) }
316+
#[cfg(feature="simd_support")]
317+
test_f64! { f64x8_edge_cases, f64x8, f64x8::splat(0.0), f64x8::splat(EPSILON64) }
206318
}

0 commit comments

Comments
 (0)