jkrasz
jkrasz

Reputation: 63

Generate random float from Standard Normal distribution and multiply by another float

Trying to generate a random number from the Standard Normal distribution. Need to multiply the value by 0.1 to get the number range i'm looking for. I tried using the documentation from rand_dist you can find here: https://docs.rs/rand_distr/0.3.0/rand_distr/struct.StandardNormal.html

My Cargo.toml is the following:

[package]
name = "test_rng"
version = "0.1.0"
authors = ["Jack"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rand = "0.7.3"
rand_distr = "0.3.0"

The starting rust code is the example provided in the rand_dist docs from above:

use rand::prelude::*;
use rand_distr::StandardNormal;

fn main() {
    let val: f64 = thread_rng().sample(StandardNormal);
    println!("{}", val);
}

When I run this it works as expected and the output is:

C:\Users\Jack\Desktop\projects\software\rust\test_rng>cargo run
   Compiling test_rng v0.1.0 (C:\Users\Jack\Desktop\projects\software\rust\test_rng)
    Finished dev [unoptimized + debuginfo] target(s) in 2.11s
     Running `target\debug\test_rng.exe`
0.48398855288705356

C:\Users\Jack\Desktop\projects\software\rust\test_rng>

This is where I'm hitting an issue, when I try to multiply the number by 0.1 in the following code I get the resulting error:

fn main() {
    let val: f64 = 0.1 * thread_rng().sample(StandardNormal);
    println!("{}", val);
}
C:\Users\Jack\Desktop\projects\software\rust\test_rng>cargo run
   Compiling test_rng v0.1.0 (C:\Users\Jack\Desktop\projects\software\rust\test_rng)
error[E0284]: type annotations needed: cannot satisfy `<f64 as std::ops::Mul<_>>::Output == f64`
 --> src\main.rs:5:24
  |
5 |     let val: f64 = 0.1 * thread_rng().sample(StandardNormal);
  |                        ^ cannot satisfy `<f64 as std::ops::Mul<_>>::Output == f64`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0284`.
error: could not compile `test_rng`.

To learn more, run the command again with --verbose.

C:\Users\Jack\Desktop\projects\software\rust\test_rng>

I tried to change 0.1 to 0.1_f64 but that gave the same error. I tried to convert random number to f64 (which it should already be) with as f64 but that resulted in the following:

fn main() {
    let val: f64 = 0.1 * thread_rng().sample(StandardNormal) as f64;
    println!("{}", val);
}
C:\Users\Jack\Desktop\projects\software\rust\test_rng>cargo run
   Compiling test_rng v0.1.0 (C:\Users\Jack\Desktop\projects\software\rust\test_rng)
error[E0282]: type annotations needed
 --> src\main.rs:5:39
  |
5 |     let val: f64 = 0.1 * thread_rng().sample(StandardNormal) as f64;
  |                                       ^^^^^^ cannot infer type for type parameter `T` declared on the associated function `sample`
  |
  = note: type must be known at this point
help: consider specifying the type arguments in the method call
  |
5 |     let val: f64 = 0.1 * thread_rng().sample::<T, D>(StandardNormal) as f64;
  |                                             ^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0282`.
error: could not compile `test_rng`.

To learn more, run the command again with --verbose.

C:\Users\Jack\Desktop\projects\software\rust\test_rng>

Thought it was a precedence issue so I tried wrapping second half in parenthesis but got the same error.

I can get it to work by making the variable mutable and separating the line into two operations like the following:

fn main() {
    let mut val: f64 = thread_rng().sample(StandardNormal);
    val *= 0.1;
    println!("{}", val);
}
C:\Users\Jack\Desktop\projects\software\rust\test_rng>cargo run
   Compiling test_rng v0.1.0 (C:\Users\Jack\Desktop\projects\software\rust\test_rng)
    Finished dev [unoptimized + debuginfo] target(s) in 1.62s
     Running `target\debug\test_rng.exe`
-0.034993448117065

C:\Users\Jack\Desktop\projects\software\rust\test_rng>

Any idea what is going on with the multiplication of the f64 with the output of the random number?

Upvotes: 6

Views: 3703

Answers (1)

PiRocks
PiRocks

Reputation: 2016

You can use the following:

fn main() {
    let val: f64 = 0.1 * thread_rng().sample::<f64,_>(StandardNormal);
    println!("{}", val);
}

This explicitly forces the sample function to return a f64. What was likely going on is that the rust type inference doesn't realize that the RHS needs to be f64, though I'm not sure exactly why.

Edit: I think some the blame here goes to the definition of sample, in that it uses an unrestricted type parameter. An MVE for this would be:

pub trait Marker{}

impl Marker for f64{}
impl Marker for f32{}

fn does_not_work<T>() -> T{
    unimplemented!()
}

fn does_work<T: Marker>() -> T{
    unimplemented!()
}

fn main() {
    let val: f64 = 0.1 * does_work();
    let val: f64 = 0.1 * does_not_work();
}

It's somewhat understandable that the compiler can't infer types for does_not_work, b/c how is it meant to know about every possible type that could multiply with f64? However of we restrict things to only certain types with a trait, then the list of possible types becomes finite and type inference works again.

Upvotes: 4

Related Questions