Skip to content

Commit

Permalink
Add format_hexf functions
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowone committed May 17, 2019
1 parent aca384a commit 8e2fb46
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ readme = "README.md"
license = "CC0-1.0"

[workspace]
members = ["parse/", "impl/"]
members = ["parse/", "impl/", "format/"]

[dependencies]
proc-macro-hack = "0.3.3"
hexf-parse = { version = "0.1.0", path = "parse/" }
hexf-impl = { version = "0.1.0", path = "impl/" }
hexf-format = { version = "0.1.0", path = "format/" }

18 changes: 18 additions & 0 deletions format/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "hexf-format"
version = "0.1.0"
authors = ["Jeong YunWon <[email protected]>"]

description = "Format hexadecimal floats (see also hexf)"
homepage = "https://github.com/lifthrasiir/hexf"
documentation = "https://docs.rs/hexf-format/"
repository = "https://github.com/lifthrasiir/hexf"
license = "CC0-1.0"

[dependencies]
num-traits = "0.2"

[dev-dependencies]
rand = "0.5"
hexf-parse = { version = "0.1.0", path = "../parse/" }

146 changes: 146 additions & 0 deletions format/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//! Format hexadecimal float literals.
//! There are two functions `format_hexf32` and `format_hexf64` provided for each type.
//!
//! ```rust
//! use hexf_format::*;
//! assert_eq!(format!("{:x}", Format(0.1f32)), "0x1.99999ap-4");
//! assert_eq!(format!("{:x}", Format(0.1f64)), "0x1.999999999999ap-4");
//! ```

extern crate num_traits;

#[cfg(test)]
extern crate hexf_parse;
#[cfg(test)]
extern crate rand;

use std::{f32, f64, fmt};

mod internal {
use num_traits::{float::Float, Signed, Zero};
use std::fmt;

pub trait FormatHexf: Signed + Float + Zero {
fn sign_string(&self) -> &'static str {
if self.is_negative() {
"-"
} else {
""
}
}
fn fmt_normal(&self, f: &mut fmt::Formatter) -> fmt::Result;
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
v if v.is_zero() => write!(f, "{}0x0.0p+0", v.sign_string()),
v if v.is_infinite() => Err(fmt::Error {}),
v if v.is_nan() => Err(fmt::Error),
_ => self.fmt_normal(f),
}
}
}

impl FormatHexf for f32 {
fn fmt_normal(&self, f: &mut fmt::Formatter) -> fmt::Result {
const BITS: i16 = 23;
const FRACT_MASK: u64 = 0x7f_ffff;
let (mantissa, exponent, _) = self.integer_decode();
write!(
f,
"{}0x{:x}.{:06x}p{:+}",
self.sign_string(),
mantissa >> BITS,
(mantissa & FRACT_MASK) << 1,
exponent + BITS
)
}
}

impl FormatHexf for f64 {
fn fmt_normal(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
const BITS: i16 = 52;
const FRACT_MASK: u64 = 0xf_ffff_ffff_ffff;
let (mantissa, exponent, _) = self.integer_decode();
write!(
f,
"{}0x{:x}.{:013x}p{:+}",
self.sign_string(),
mantissa >> BITS,
mantissa & FRACT_MASK,
exponent + BITS
)
}
}

}

pub struct Format<T>(pub T);

impl<F: internal::FormatHexf> fmt::LowerHex for Format<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}

/// Tries to format an `f32` to hexadecimal.
pub fn format_hexf32(value: f32) -> String {
format!("{:x}", Format(value))
}

/// Tries to format an `f64` to hexadecimal.
pub fn format_hexf64(value: f64) -> String {
format!("{:x}", Format(value))
}

#[test]
fn test_format_normal_hexf32() {
use hexf_parse::parse_hexf32;
use rand::Rng;

for _ in 0..20000 {
let bytes = rand::thread_rng().gen::<[u32; 1]>();
let f = f32::from_bits(bytes[0]);
if !f.is_finite() {
continue;
}

let hex = format!("{:x}", Format(f));
// println!("{} -> {}", f, hex);
let roundtrip = parse_hexf32(&hex, false).unwrap();
// println!(" -> {}", roundtrip);
assert_eq!(f, roundtrip, "{} {} {}", f, hex, roundtrip);
}
}

#[test]
fn test_format_normal_hexf64() {
use hexf_parse::parse_hexf64;
use rand::Rng;

for _ in 0..20000 {
let bytes = rand::thread_rng().gen::<[u64; 1]>();
let f = f64::from_bits(bytes[0]);
if !f.is_finite() {
continue;
}
let hex = format!("{:x}", Format(f));
// println!("{} -> {}", f, hex);
let roundtrip = parse_hexf64(&hex, false).unwrap();
// println!(" -> {}", roundtrip);
assert_eq!(f, roundtrip, "{} {} {}", f, hex, roundtrip);
}
}

#[test]
fn test_format_hexf() {
assert_eq!(format!("{:x}", Format(0.0f64)), "0x0.0p+0");
assert_eq!(format!("{:x}", Format(0.0f32)), "0x0.0p+0");
assert_eq!(format!("{:x}", Format(-0.0f64)), "-0x0.0p+0");
assert_eq!(format!("{:x}", Format(-0.0f32)), "-0x0.0p+0");

// assert_eq!(f64::INFINITY.format().unwrap_err(), ERR_INFINITY);
// assert_eq!(format(f64::NEG_INFINITY).unwrap_err(), ERR_INFINITY);
// assert_eq!(format(f64::NAN).unwrap_err(), ERR_NAN);
// assert_eq!(format(f32::INFINITY).unwrap_err(), ERR_INFINITY);
// assert_eq!(format(f32::NEG_INFINITY).unwrap_err(), ERR_INFINITY);
// assert_eq!(format(f32::NAN).unwrap_err(), ERR_NAN);
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
#[macro_use] extern crate proc_macro_hack;
#[macro_use] #[allow(unused_imports)] extern crate hexf_impl;
extern crate hexf_parse;
extern crate hexf_format;

pub use hexf_parse::{ParseHexfError, parse_hexf32, parse_hexf64};
#[doc(hidden)] pub use hexf_impl::*;
pub use hexf_format::{FormatHexfError, format_hexf32, format_hexf64};

proc_macro_expr_decl! {
/// Expands to a `f32` value with given hexadecimal representation.
Expand Down

0 comments on commit 8e2fb46

Please sign in to comment.