问题
I want to decode, store and encode a float in Rust. I know about num::Float::integer_decode() but I'd rather not lose any precision. That is, unless the format I encode into is smaller than the format I encode from of course.
回答1:
Interpret the floating point bits as an integer and print out the value as 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);
}
回答2:
What's wrong with integer_decode()
? It is lossless and works for finite numbers as well as NaN and infinities:
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));
}
回答3:
If you don't intend to transfer serialized data between machines or you're certain that float representation is the same on all platforms you target, you can store byte representation of the number:
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);
}
}
You can also use existing serialization framework, like serde. If you don't want the entire framework and just want to serialize floats, you can use dtoa (it's used by serde_json), although I'm not sure if it provides strong precision guarantees.
来源:https://stackoverflow.com/questions/40030551/how-to-decode-and-encode-a-float-in-rust