Skip to content

Commit 03b1b99

Browse files
committed
Add sample_single_inclusive_canon_64
This beats other uniform_int_i128 results
1 parent e651769 commit 03b1b99

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

benches/uniform.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ macro_rules! bench_int {
7070
bench_int_group!("Old", $T, sample_single_inclusive, g, inputs);
7171
bench_int_group!("ONeill", $T, sample_single_inclusive_oneill, g, inputs);
7272
bench_int_group!("Canon", $T, sample_single_inclusive_canon, g, inputs);
73+
bench_int_group!("Canon64", $T, sample_single_inclusive_canon_64, g, inputs);
7374
bench_int_group!("Canon-Lemire", $T, sample_inclusive_canon_lemire, g, inputs);
7475
bench_int_group!("Bitmask", $T, sample_single_inclusive_bitmask, g, inputs);
7576
}};

src/distributions/uniform/uniform_int.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,8 @@ macro_rules! uniform_int_64_impl {
465465
($ty:ty, $unsigned:ident) => {
466466
impl UniformInt<$ty> {
467467
/// Sample, Canon's method variant
468+
///
469+
/// Variant: potential increase to bias (uses a single `u64` sample).
468470
#[inline]
469471
pub fn sample_canon_64<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
470472
let range = self.range as $unsigned as u64;
@@ -476,6 +478,35 @@ macro_rules! uniform_int_64_impl {
476478
// bias is at most 1 in 2.pow(56) for i8, 1 in 2.pow(48) for i16
477479
self.low.wrapping_add(result as $ty)
478480
}
481+
482+
/// Sample single inclusive, using Canon's method variant
483+
///
484+
/// Variant: potential increase to bias (uses a single `u64` sample).
485+
#[inline]
486+
pub fn sample_single_inclusive_canon_64<R: Rng + ?Sized, B1, B2>(
487+
low_b: B1, high_b: B2, rng: &mut R,
488+
) -> $ty
489+
where
490+
B1: SampleBorrow<$ty> + Sized,
491+
B2: SampleBorrow<$ty> + Sized,
492+
{
493+
let low = *low_b.borrow();
494+
let high = *high_b.borrow();
495+
assert!(
496+
low <= high,
497+
"UniformSampler::sample_single_inclusive: low > high"
498+
);
499+
let range = high.wrapping_sub(low).wrapping_add(1) as $unsigned as u64;
500+
if range == 0 {
501+
// Range is MAX+1 (unrepresentable), so we need a special case
502+
return rng.gen();
503+
}
504+
505+
// generate a sample using a sensible integer type
506+
let (result, _lo1) = rng.gen::<u64>().wmul(range);
507+
// bias is at most 1 in 2.pow(56) for i8, 1 in 2.pow(48) for i16
508+
low.wrapping_add(result as $ty)
509+
}
479510
}
480511
}
481512
}
@@ -490,6 +521,18 @@ macro_rules! uniform_int_64void_impl {
490521
pub fn sample_canon_64<R: Rng + ?Sized>(&self, _rng: &mut R) -> $ty {
491522
Default::default() // not used
492523
}
524+
525+
/// Sample single inclusive, using Canon's method variant
526+
#[inline]
527+
pub fn sample_single_inclusive_canon_64<R: Rng + ?Sized, B1, B2>(
528+
_low_b: B1, _high_b: B2, _rng: &mut R,
529+
) -> $ty
530+
where
531+
B1: SampleBorrow<$ty> + Sized,
532+
B2: SampleBorrow<$ty> + Sized,
533+
{
534+
Default::default() // not used
535+
}
493536
}
494537
}
495538
}
@@ -519,6 +562,44 @@ impl UniformInt<i128> {
519562

520563
self.low.wrapping_add(result as i128)
521564
}
565+
566+
/// Sample single inclusive, using Canon's method variant
567+
///
568+
/// Variant: potential increase to bias (uses a single `u64` sample).
569+
#[inline]
570+
pub fn sample_single_inclusive_canon_64<R: Rng + ?Sized, B1, B2>(
571+
low_b: B1, high_b: B2, rng: &mut R,
572+
) -> i128
573+
where
574+
B1: SampleBorrow<i128> + Sized,
575+
B2: SampleBorrow<i128> + Sized,
576+
{
577+
let low = *low_b.borrow();
578+
let high = *high_b.borrow();
579+
assert!(
580+
low <= high,
581+
"UniformSampler::sample_single_inclusive: low > high"
582+
);
583+
let range = high.wrapping_sub(low).wrapping_add(1) as u128;
584+
if range == 0 {
585+
// Range is MAX+1 (unrepresentable), so we need a special case
586+
return rng.gen();
587+
}
588+
589+
// generate a sample using a sensible integer type
590+
let (mut result, lo1) = rng.gen::<u128>().wmul(range);
591+
592+
if lo1 > range.wrapping_neg() {
593+
// Generate more bits. Sample is multiplied by 2.pow(-192), so
594+
// hi2 is multiplied by 2.pow(-64):
595+
let (hi2, lo2) = (rng.gen::<u64>() as u128).wmul(range);
596+
debug_assert_eq!(hi2 >> 64, 0u128);
597+
let is_overflow = lo1.checked_add((hi2 << 64) | (lo2 >> 64)).is_none();
598+
result += is_overflow as u128;
599+
}
600+
601+
low.wrapping_add(result as i128)
602+
}
522603
}
523604

524605
#[cfg(feature = "simd_support")]

0 commit comments

Comments
 (0)