Floating point 如何解码和编码生锈的浮子?
我想解码,存储和编码一个生锈的浮动。我知道,但我不想失去任何精确性。也就是说,除非我编码的格式小于我编码的格式。将浮点位解释为整数,并将值打印为十六进制:Floating point 如何解码和编码生锈的浮子?,floating-point,rust,decode,encode,Floating Point,Rust,Decode,Encode,我想解码,存储和编码一个生锈的浮动。我知道,但我不想失去任何精确性。也就是说,除非我编码的格式小于我编码的格式。将浮点位解释为整数,并将值打印为十六进制: 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 = for
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);
}
integer\u 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));
}
使用std::mem;
fn整数解码(val:f64)->(u64,i16,i8){
let位:u64=不安全{mem::transmute(val)};
让符号:i8=如果位>>63==0{1}否则{-1};
让mut指数:i16=((位>>52)&0x7ff)作为i16;
设尾数=如果指数=0{
(bits&0xfffffffffffffff)如果您不打算在机器之间传输序列化数据,或者您确定在所有目标平台上的浮点表示都是相同的,则可以存储数字的字节表示:
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_json使用它),尽管我不确定它是否提供了强大的精度保证。较新版本的Rust提供了比其他一些答案更安全的选项:
- 在Rust 1.20中,您可以使用和与
u64
二进制表示法进行转换
- 从Rust 1.40开始,您可以使用和处理
[u8;8]
(还有一些方法用于小尾端字节顺序和本机字节顺序。)
整数解码()
不会丢失精度——这只是将浮点数分解为其组成部分。@trentcl我知道,但它似乎与上述函数的文档中的编码一样。从函数附带的文档来看,它可能会在编码过程中丢失精度。@JeroenBollen你能链接这些文档吗?我不能在std
中找不到这些信息。我实现了fn integer\u encode((尾数,指数,符号):(u64,i16,i8))->f64{(符号为f64)*(尾数为f64)*(2f64.powf(指数为f64))
并且它适用于任何X
输入integer\u encode(integer\u decode(X))
除了NaN(也许我的integer\u encode
有问题。我在OP中链接过它。@JeroenBollen该链接没有提供任何关于精度损失的信息。@JeroenBollen可能这只是f32
的一种情况?我无法重现f64
的任何差异,至少不足以破坏断言的eq!(2.0,integer_encode(integer_decode(2.0));
与您链接的文档中的示例类似。为什么不将f64
转换为[u8;8]
并在不进行不必要转换的情况下存储它?@PavelStrakhov听起来是回答问题的最佳人选!哦!我希望f32
和f64
可以直接编码为十六进制(这是获得便携表示的最简单方法)但是很明显,它们没有实现LowerHex
或UpperHex
。失望:(@MatthieuM.是的,而且也没有十六进制浮点文字,所以如果需要一个非常特定的浮点,就必须通过转换
:-(@Shepmaster:我认为在这里提出一个RFC是值得的;它支持文本和格式化/解析。格式化/解析比十进制格式简单得多;但是,文本解析可能需要一些技巧(以避免整数的歧义)。