Skip to content

Panic in Binomial::sample with small p and n close to u64::MAX #21

@mstoeckl

Description

@mstoeckl

Summary

I observe that when n is a large fraction of u64::MAX and p is a small floating point value, Binomial::sample sometimes panics with the following error:

thread 'main' panicked at /playground/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rand_distr-0.5.1/src/binomial.rs:174:5:
assertion failed: x < (i64::MAX as f64)
stack backtrace:
   0: rust_begin_unwind
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:692:5
   1: core::panicking::panic_fmt
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:75:14
   2: core::panicking::panic
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:145:5
   3: rand_distr::binomial::f64_to_i64
             at ./.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rand_distr-0.5.1/src/binomial.rs:174:5
   4: rand_distr::binomial::btpe
             at ./.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rand_distr-0.5.1/src/binomial.rs:344:18
   5: <rand_distr::binomial::Binomial as rand::distr::distribution::Distribution<u64>>::sample
             at ./.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rand_distr-0.5.1/src/binomial.rs:385:49
   6: playground::main
             at ./src/main.rs:19:17
   7: core::ops::function::FnOnce::call_once
             at ./.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

I was using current stable rust (1.85) and rand_distr 0.5.1, rand + rand_chacha v0.9.0, on x86_64 (Ryzen 7 7840 HS). The executable is linked to libm from the Arch Linux build of glibc, version 2.41+r9+ga900dbaf70f0-1.

Code sample:

Starting from a specific random generator state, the issue can be reproduced at the Rust Playground with both Stable and Nightly compilers.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=da487cdb127284b69d5e74a2f57c9748

use rand::SeedableRng;
use rand_chacha::ChaCha12Rng;
use rand_distr::{Binomial, Distribution};

fn main() {
    let n: u64 = 13301667360732270876;
    let p: f64 = 6.099468852510934e-7;
    let seed: [u8; 32] = [
        65, 56, 42, 90, 76, 75, 12, 151, 226, 205, 74, 251, 40, 152, 42, 207, 114, 35, 176, 234,
        170, 225, 85, 155, 136, 8, 223, 69, 98, 184, 87, 212,
    ];
    let pos: u128 = 1940220;
    let stream: u64 = 0;

    let mut rng = ChaCha12Rng::from_seed(seed);
    rng.set_stream(stream);
    rng.set_word_pos(pos);
    let bin = Binomial::new(n, p).unwrap();
    let value = bin.sample(&mut rng);
    println!("Output: {}", value);
}

Let me know if you'd like me to provide more information; I will try to debug this further if I ever have enough free time.

Workaround

In case anyone else is affected by this edge case, my current workaround is to use the fact that Binomial(n + m, p) can be sampled by sampling x ~ Binomial(n, p), y ~ Binomial(m, p), and outputting x+y (or adding together more copies of smaller distributions, if necessary; I have yet to experimentally verify an exact upper bound for n below which this error does not occur. n <= 1<<62 appears fine so far.)

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions