From 77f1a8adfdb8810194b083a5cc53cdfea20bf1da Mon Sep 17 00:00:00 2001 From: Pyfisch Date: Sun, 22 Jul 2018 21:17:59 +0200 Subject: [PATCH] Add half (f16) encoding Add a dependency on the excellent half crate. --- Cargo.toml | 1 + src/de.rs | 21 ++------------------- src/lib.rs | 1 + src/ser.rs | 6 +++++- tests/ser.rs | 7 +++++++ 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ad39a463..def4b792 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ categories = ["encoding"] [dependencies] byteorder = "1.0.0" +half = "1.1.2" serde = "1.0.14" [dev-dependencies] diff --git a/src/de.rs b/src/de.rs index f8b6ecb7..544e9765 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1,6 +1,7 @@ //! Deserialization. use byteorder::{ByteOrder, BigEndian}; +use half::f16; use serde::de; use std::io; use std::str; @@ -366,25 +367,7 @@ where } fn parse_f16(&mut self) -> Result { - let half = self.parse_u16()?; - let exp = (half >> 10) & 0x1f; - let mant = half & 0x3ff; - - let mut val = if exp == 0 { - (mant as f32) * (-24f32).exp2() - } else if exp != 31 { - ((mant + 1024) as f32) * (exp as f32 - 25.).exp2() - } else if mant == 0 { - f32::INFINITY - } else { - f32::NAN - }; - - if half & 0x8000 != 0 { - val = -val; - } - - Ok(val) + Ok(f32::from(f16::from_bits(self.parse_u16()?))) } fn parse_f32(&mut self) -> Result { diff --git a/src/lib.rs b/src/lib.rs index 947a75bb..837af77d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -148,6 +148,7 @@ #![deny(missing_docs)] extern crate byteorder; +extern crate half; #[macro_use] extern crate serde; diff --git a/src/ser.rs b/src/ser.rs index be837607..b0b35b47 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -1,5 +1,6 @@ //! Serialize a Rust data structure to CBOR data. use byteorder::{ByteOrder, BigEndian}; +use half::f16; use serde::ser::{self, Serialize}; use std::io; @@ -298,8 +299,11 @@ where } } else if value.is_nan() { self.writer.write_all(&[0xf9, 0x7e, 0x00]) + } else if f32::from(f16::from_f32(value)) == value { + let mut buf = [0xf9, 0, 0]; + BigEndian::write_u16(&mut buf[1..], f16::from_f32(value).as_bits()); + self.writer.write_all(&buf) } else { - // TODO encode as f16 when possible let mut buf = [0xfa, 0, 0, 0, 0]; BigEndian::write_f32(&mut buf[1..], value); self.writer.write_all(&buf) diff --git a/tests/ser.rs b/tests/ser.rs index 2de7d1d9..68069887 100644 --- a/tests/ser.rs +++ b/tests/ser.rs @@ -142,3 +142,10 @@ fn test_byte_string() { // byte strings > 2^32 bytes have 9-byte headers, but they take too much RAM // to test in Travis. } + +#[test] +fn test_half() { + let vec = to_vec(&42.5f32).unwrap(); + assert_eq!(vec, b"\xF9\x51\x50"); + assert_eq!(from_slice::(&vec[..]).unwrap(), 42.5f32); +}