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是值得的;它支持文本和格式化/解析。格式化/解析比十进制格式简单得多;但是,文本解析可能需要一些技巧(以避免整数的歧义)。