Generic numeric conversion

Hey All,

This one has me stumped. I'm trying to write a generic function that takes a vector of integers and converts them to f64 by applying an offset and scaling factor. The integers can be signed or unsigned, of any width, like u8, u16, i32, i64, etc. Something like this:

fn convert_vec<T>(raw: &Vec<T>, offset: f64, scale: f64) -> Vec<f64> 
  where T: ...
{
    let data: Vec<f64> = raw.iter()
                            .map(|&x| (f64::from(x) + offset) * scale)
                            .collect();
    data
}

I can't figure out how to constrain T to get this (or something equivalent) to compile.

Thanks!

T: Into<f64> will work for widths up to 32-bit, but not for 64-bit integers (which can't all be converted losslessly to f64).

The num-traits or cast crates have conversion traits that allow lossy conversions from int to float. (They just use as casts.)

Playground.

Yeah, Into<f64> was the first thing I tried. (I assumed I would have to work around the i64 case). But it gave an error:

the trait `std::convert::From<T>` is not implemented for `f64`

Playground (Into<f64>)

But regardless, your solution looks even better! Thanks @mbrubeck.

I can't beat the existing solution, but I'll point out that you can place constraints on any type, not just on generics. Here, you have required T: Into<f64> and then you are calling f64::from. From and Into auto-implement each other, but this doesn't happen in generic constraints. You can get your playground link to compile by requiring either of the following:

f64: From<T>, T: Copy

T: Into<f64> + Copy

Ah, funny. I tried that second one in my real code (which is a larger function), hit a lifetime error, and thought it was related to this. Glad to see that it actually works.

But that first one...

f64: From<T> ...

That's awesome! I had no idea you could do that. Thanks, @frsrblch.

It's feasible this way:

trait ApproxFrom<T> {
    fn approx_from(x: T) -> Self;
}

impl ApproxFrom<u8> for f64 {
    fn approx_from(x: u8) -> Self {f64::from(x)}
}
impl ApproxFrom<u16> for f64 {
    fn approx_from(x: u16) -> Self {f64::from(x)}
}
impl ApproxFrom<i32> for f64 {
    fn approx_from(x: i32) -> Self {f64::from(x)}
}
impl ApproxFrom<i64> for f64 {
    fn approx_from(x: i64) -> Self {x as f64}
}

fn convert_slice<T>(raw: &[T], offset: f64, scale: f64) -> Vec<f64> 
where T: Copy, f64: ApproxFrom<T>
{
    raw.iter().map(|&x| (f64::approx_from(x) + offset) * scale)
        .collect::<Vec<f64>>()
}

fn main() {
    let a: Vec<i64> = vec![1,2,3,4];
    println!("{:?}",convert_slice(&a,0.0,1.0));
}

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.