How to read a generic float (f64 or f32) from a file to a buffer

My current code only works on f64 and calls:
buf_reader.read_f64_into::<LittleEndian>(&mut dst)
I'd like to make it generic for f64 and f32.
There is a
buf_reader.read_f32_into::<LittleEndian>(&mut dst)
but there is no
read_float_into<T: float>(&mut dst)

Maybe I should define a new ReadFloatInto trait with implementations for f64 and f32, but I don't know how. I'd welcome any help.

Thanks!
Carl

p.s. We can assume LittleEndian. This is for an open source tool used in genomics, FaST-LMM.

It will depend on what type buf_reader has, but what about something like this?

use byte_order::{BigEndian, LittleEndian, ByteOrder, ReadBytesExt};

trait ReadFloat<Endian>: Sized {
  fn read_float<R>(buffer: R) -> std::io::Result<Self>
  where 
    R: Read;
}

impl<Endian: ByteOrder> ReadFloat<Endian> for f32 {
  fn read_float<R>(buffer: R) -> std::io::Result<Self>
  where 
    R: Read
  {
    buffer.read_f32<Endian>()
  }
}

fn main() {
  let mut f = std::fs::File::Open("...");

  let float = f32::read_float::<BigEndian>(&mut f).unwrap();
  let double = f64::read_float::<LittleEndian>(&mut f).unwrap();
}

The key elements are:

  • ReadFloat is generic over endianness
  • We accept any R: Read because it will automatically implement byte_order::ReadBytesExt and let you work with buffers, files, or whatever
  • Implementing for f64 is an exercise for the reader

Thanks! I'd love to find something that works from buffer to buffer. I just figured out Rust Playground, so I can ask more clearly. This shows how ReadBytesExt works now and the comments show how I'd like to use it.

use std::f32;
use std::f64;
use std::io::Cursor;

use byteorder::{BigEndian, ReadBytesExt};

fn main() {
    let mut dst32 = [0f32; 2];
    let mut rdr = Cursor::new(vec![0x40, 0x49, 0x0f, 0xdb, 0x3f, 0x80, 0x00, 0x00]);
    rdr.read_f32_into::<BigEndian>(&mut dst32).unwrap();
    assert_eq!([f32::consts::PI, 1.0], dst32);
    
    let mut rdr = Cursor::new(vec![0x40, 0x49, 0x0f, 0xdb, 0x3f, 0x80, 0x00, 0x00]);
    rdr.read_f32_into::<BigEndian>(&mut dst32).unwrap();
    assert_eq!([f32::consts::PI, 1.0], dst32);

    let mut dst64 = [0f64; 2];
    let mut rdr = Cursor::new(vec![
        0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18, 0x3f, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00,
    ]);
    rdr.read_f64_into::<BigEndian>(&mut dst64).unwrap();
    assert_eq!([f64::consts::PI, 1.0], dst64);

    // Can we make a generic read_into that works with f32,f64 destinations?
    /*
    let mut rdr = Cursor::new(vec![0x40, 0x49, 0x0f, 0xdb, 0x3f, 0x80, 0x00, 0x00]);
    rdr.read_into::<BigEndian>(&mut dst32).unwrap(); // ideal
    // dst32.read_into::<BigEndian>(rdr).unwrap(); OK, too
    assert_eq!([f32::consts::PI, 1.0], dst32);
    
    let mut rdr = Cursor::new(vec![
        0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18, 0x3f, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00,
    ]);
    rdr.read_into::<BigEndian>(&mut dst64).unwrap(); // ideal
    // dst64.read_into::<BigEndian>(rdr).unwrap(); OK, too
    assert_eq!([f64::consts::PI, 1.0], dst64);
    
    */
    
}

(Playground)

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.