Как декодировать и кодировать поплавок в Rust?

Я хочу декодировать, хранить и кодировать поплавок в Rust. Я знаю о num::Float::integer_decode() но я бы предпочел не терять точность. То есть, если формат, в который я кодирую, меньше, чем формат, из которого я, конечно, кодирую.

3 ответа

Новые версии Rust предоставляют более безопасные варианты, чем предлагают некоторые другие ответы:

  • Начиная с Rust 1.20, вы можете использовать to_bits и from_bits для преобразования в и изu64 двоичное представление.
  • Начиная с Rust 1.40 вы можете использовать to_be_bytes и from_be_bytes для работы с[u8; 8]. (Существуют также методы для порядка байтов с прямым порядком байтов и собственного порядка байтов.)

Интерпретировать биты с плавающей запятой как целое число и вывести значение как шестнадцатеричное:

use std::mem;

fn main() {
    let a_third: f64 = 1.0 / 3.0;

    let as_int: u64 = unsafe { mem::transmute(a_third) };
    println!("{}", as_int);

    let as_string = format!("{:016x}", as_int);
    println!("{}", as_string);

    let back_to_int = u64::from_str_radix(&as_string, 16).expect("Not an integer");
    println!("{}", back_to_int);

    let back_to_float: f64 = unsafe { mem::transmute(back_to_int) };
    println!("{}", back_to_float);

    assert_eq!(back_to_float, a_third);
}

Если вы не собираетесь передавать сериализованные данные между компьютерами или уверены, что представление с плавающей запятой одинаково на всех целевых платформах, вы можете сохранить байтовое представление числа:

use std::io::{Read, Write};

fn main() {
  {
    let num: f64 = 1.0 / 3.0;
    let bytes: [u8; 8] = unsafe { std::mem::transmute(num) };
    let mut file = std::fs::File::create("/tmp/1").unwrap();
    file.write_all(&bytes).unwrap();
  }
  {
    let mut file = std::fs::File::open("/tmp/1").unwrap();
    let mut bytes: [u8; 8] = unsafe { std::mem::uninitialized() };
    file.read_exact(&mut bytes).unwrap();
    let num: f64 = unsafe { std::mem::transmute(bytes) };
    println!("result: {}", num);
  }
}

Вы также можете использовать существующую платформу сериализации, например, serde. Если вам не нужен весь фреймворк и вы хотите просто сериализовать float, вы можете использовать dtoa (он используется serde_json), хотя я не уверен, обеспечивает ли он строгие гарантии точности.

Что случилось с integer_decode()? Это без потерь и работает для конечных чисел, а также NaN и бесконечности:

use std::mem;

fn integer_decode(val: f64) -> (u64, i16, i8) {
    let bits: u64 = unsafe { mem::transmute(val) };
    let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 };
    let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
    let mantissa = if exponent == 0 {
        (bits & 0xfffffffffffff) << 1
    } else {
        (bits & 0xfffffffffffff) | 0x10000000000000
    };

    exponent -= 1023 + 52;
    (mantissa, exponent, sign)
}

fn main() {
    println!("{:?}", integer_decode(std::f64::NAN));
    println!("{:?}", integer_decode(std::f64::INFINITY));
    println!("{:?}", integer_decode(std::f64::NEG_INFINITY));
}
Другие вопросы по тегам