2016-10-13 4 views
1

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

+2

'integer_decode()' не теряет точность - это просто деконструкция числа с плавающей запятой в его компонентах. – trentcl

+0

@trentcl Я знаю, но он выглядит как кодировка, как в документации к вышеупомянутой функции. –

ответ

2

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

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); 
} 
+3

Почему бы просто не трансмутировать 'f64' в' [u8; 8] 'и хранить его без ненужных преобразований? –

+0

@PavelStrakhov звучит как главный кандидат на ответ! – Shepmaster

+0

О! Я надеялся, что 'f32' и' f64' будут напрямую кодироваться как шестнадцатеричные (это самый простой способ получить переносимое представление), но, по-видимому, они не реализуют 'LowerHex' или' UpperHex'. Разочарование :( –

1

Что случилось с 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)); 
} 
+0

Исходя из документов, которые поставляются с функция, она может потерять точность во время кодирования –

+0

@JeroenBollen вы можете связать эти документы? Я не могу найти эту информацию в 'std'. Я реализовал' fn integer_encode ((mantissa, exponent, sign): (u64, i16, i8)) -> f64 {(знак как f64) * (mantissa as f64) * (2f64.powf (exponent as f64))} 'и он работает для любого' X', который я передаю в 'integer_encode (integer_decode (X)) 'за исключением' NaN' (возможно, что-то не так с моим 'int eger_encode', хотя). – ljedrz

+0

Я сделал ссылку на него в OP. –

1

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

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. Если вы не хотите всю структуру и просто хотите сериализовать поплавки, вы можете использовать dtoa (она используется serde_json), хотя я не уверен, что она обеспечивает надежные гарантии точности.

Смежные вопросы