add f16 feature

This commit is contained in:
Trevor Spiteri 2018-08-23 13:12:32 +02:00
parent f7b4242102
commit 932caf3f92
6 changed files with 325 additions and 13 deletions

View File

@ -17,5 +17,15 @@ keywords = ["mathematics", "numerics"]
categories = ["algorithms", "data-structures", "no-std", "science"]
license = "MIT/Apache-2.0"
[features]
f16 = ["half"]
[dependencies]
typenum = "1.8"
[dependencies.half]
version = "1"
optional = true
[package.metadata.docs.rs]
features = ["f16"]

View File

@ -35,6 +35,14 @@ fixed-point numbers.
## Whats new
### Version 0.1.3 news (unreleased)
* The `f16` feature was added with its methods [`from_f16`] and
[`to_f16`].
[`from_f16`]: https://docs.rs/fixed/0.1.3/fixed/struct.FixedI32.html#method.from_f16
[`to_f16`]: https://docs.rs/fixed/0.1.3/fixed/struct.FixedI32.html#method.to_f16
### Version 0.1.2 news (2018-08-15)
* The crate can now be used without the standard library `std`.
@ -108,6 +116,22 @@ extern crate fixed;
The *fixed* crate requires rustc version 1.28.0 or later.
## Optional features
The *fixed* crate has one optional feature:
1. `f16`, disabled by default. This provides conversion to/from
[`f16`]. This features requires the [*half* crate].
To enable the feature, you can add the dependency like this to
[*Cargo.toml*]:
```toml
[dependencies.fixed]
version = "0.1.2"
features = ["f16"]
```
## License
This crate is free software: you can redistribute it and/or modify it
@ -127,6 +151,7 @@ additional terms or conditions.
[*Cargo.toml*]: https://doc.rust-lang.org/cargo/guide/dependencies.html
[*fixed* crate]: https://crates.io/crates/fixed
[*half* crate]: https://crates.io/crates/half
[*typenum* crate]: https://crates.io/crates/typenum
[LICENSE-APACHE]: https://www.apache.org/licenses/LICENSE-2.0
[LICENSE-MIT]: https://opensource.org/licenses/MIT
@ -140,5 +165,6 @@ additional terms or conditions.
[`FixedU32`]: https://docs.rs/fixed/0.1.2/fixed/struct.FixedU32.html
[`FixedU64`]: https://docs.rs/fixed/0.1.2/fixed/struct.FixedU64.html
[`FixedU8`]: https://docs.rs/fixed/0.1.2/fixed/struct.FixedU8.html
[`f16`]: https://docs.rs/half/^1/half/struct.f16.html
[channels]: https://doc.rust-lang.org/book/second-edition/appendix-07-nightly-rust.html
[const generics]: https://github.com/rust-lang/rust/issues/44580

View File

@ -5,6 +5,12 @@ modification, are permitted in any medium without royalty provided the
copyright notice and this notice are preserved. This file is offered
as-is, without any warranty. -->
Version 0.1.3 news (unreleased)
==========================
* The `f16` feature was added with its methods `from_f16` and
`to_f16`.
Version 0.1.2 (2018-08-15)
==========================

View File

@ -15,6 +15,8 @@
use core::mem;
use frac::{IsLessOrEqual, True, Unsigned, U16, U32, U8};
#[cfg(feature = "f16")]
use half::f16;
use helper::FloatHelper;
use {FixedI16, FixedI32, FixedI8, FixedU16, FixedU32, FixedU8};
@ -149,8 +151,12 @@ macro_rules! to_float {
}
pub(crate) trait FloatConv: Sized {
#[cfg(feature = "f16")]
fn from_f16(val: f16, frac_bits: u32) -> Option<(Self, bool)>;
fn from_f32(val: f32, frac_bits: u32) -> Option<(Self, bool)>;
fn from_f64(val: f64, frac_bits: u32) -> Option<(Self, bool)>;
#[cfg(feature = "f16")]
fn to_f16(self, neg: bool, frac_bits: u32) -> f16;
fn to_f32(self, neg: bool, frac_bits: u32) -> f32;
fn to_f64(self, neg: bool, frac_bits: u32) -> f64;
}
@ -158,8 +164,12 @@ pub(crate) trait FloatConv: Sized {
macro_rules! float_conv {
($($Uns:ty)*) => { $(
impl FloatConv for $Uns {
#[cfg(feature = "f16")]
from_float! { fn from_f16(f16) -> $Uns }
from_float! { fn from_f32(f32) -> $Uns }
from_float! { fn from_f64(f64) -> $Uns }
#[cfg(feature = "f16")]
to_float! { fn to_f16($Uns) -> f16 }
to_float! { fn to_f32($Uns) -> f32 }
to_float! { fn to_f64($Uns) -> f64 }
}
@ -181,6 +191,10 @@ macro_rules! lossless_from_fixed {
};
}
#[cfg(feature = "f16")]
lossless_from_fixed! { FixedI8(U8)::to_f16 -> f16 }
#[cfg(feature = "f16")]
lossless_from_fixed! { FixedU8(U8)::to_f16 -> f16 }
lossless_from_fixed! { FixedI8(U8)::to_f32 -> f32 }
lossless_from_fixed! { FixedI16(U16)::to_f32 -> f32 }
lossless_from_fixed! { FixedU8(U8)::to_f32 -> f32 }
@ -278,6 +292,53 @@ mod tests {
assert!(Fix::from_f32(511.0 / 32.0).is_none());
}
#[cfg(feature = "f16")]
#[test]
fn to_f16() {
use half::f16;
for u in 0x00..=0xff {
let fu = FixedU8::<frac::U7>::from_bits(u);
assert_eq!(fu.to_f16(), f16::from_f32(u as f32 / 128.0));
let i = u as i8;
let fi = FixedI8::<frac::U7>::from_bits(i);
assert_eq!(fi.to_f16(), f16::from_f32(i as f32 / 128.0));
for hi in &[
0u32,
0x0000_0100,
0x7fff_ff00,
0x8000_0000,
0x8100_0000,
0xffff_fe00,
0xffff_ff00,
] {
let uu = *hi | u as u32;
let fuu = FixedU32::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_f16(), f16::from_f32(uu as f32 / 128.0));
let ii = uu as i32;
let fii = FixedI32::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_f16(), f16::from_f32(ii as f32 / 128.0));
}
for hi in &[
0u128,
0x0000_0000_0000_0000_0000_0000_0000_0100,
0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ff00,
0x8000_0000_0000_0000_0000_0000_0000_0000,
0x8100_0000_0000_0000_0000_0000_0000_0000,
0xffff_ffff_ffff_ffff_ffff_ffff_ffff_fe00,
0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ff00,
] {
let uu = *hi | u as u128;
let fuu = FixedU128::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_f16(), f16::from_f64(uu as f64 / 128.0));
let ii = uu as i128;
let fii = FixedI128::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_f16(), f16::from_f64(ii as f64 / 128.0));
}
}
}
#[test]
fn to_f32() {
for u in 0x00..=0xff {

View File

@ -15,6 +15,8 @@
use core::mem;
use frac::{IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8};
#[cfg(feature = "f16")]
use half::f16;
use {
FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64,
FixedU8,
@ -35,7 +37,7 @@ pub(crate) trait FloatHelper {
}
macro_rules! float_helper {
($Float:ident($Bits:ty, $prec:expr)) => {
($Float:ident($Bits:ty, $prec:expr, $to_bits:ident)) => {
impl FloatHelper for $Float {
type Bits = $Bits;
@ -98,7 +100,7 @@ macro_rules! float_helper {
let mant_mask = !(!0 << ($prec - 1));
let exp_mask = !(neg_mask | mant_mask);
let bits = self.to_bits();
let bits = self.$to_bits();
let neg = bits & neg_mask != 0;
let biased_exp = (bits & exp_mask) >> ($prec - 1);
let exp = (biased_exp as i32) - <$Float as FloatHelper>::exp_bias();
@ -110,8 +112,10 @@ macro_rules! float_helper {
};
}
float_helper! { f32(u32, 24) }
float_helper! { f64(u64, 53) }
#[cfg(feature = "f16")]
float_helper! { f16(u16, 11, as_bits) }
float_helper! { f32(u32, 24, to_bits) }
float_helper! { f64(u64, 53, to_bits) }
pub(crate) trait FixedHelper<Frac>: Sized
where

View File

@ -77,6 +77,22 @@ extern crate fixed;
The *fixed* crate requires rustc version 1.28.0 or later.
## Optional features
The *fixed* crate has one optional feature:
1. `f16`, disabled by default. This provides conversion to/from
[`f16`]. This features requires the [*half* crate].
To enable the feature, you can add the dependency like this to
[*Cargo.toml*]:
```toml
[dependencies.fixed]
version = "0.1.2"
features = ["f16"]
```
## License
This crate is free software: you can redistribute it and/or modify it
@ -96,6 +112,7 @@ additional terms or conditions.
[*Cargo.toml*]: https://doc.rust-lang.org/cargo/guide/dependencies.html
[*fixed* crate]: https://crates.io/crates/fixed
[*half* crate]: https://crates.io/crates/half
[*typenum* crate]: https://crates.io/crates/typenum
[LICENSE-APACHE]: https://www.apache.org/licenses/LICENSE-2.0
[LICENSE-MIT]: https://opensource.org/licenses/MIT
@ -109,6 +126,7 @@ additional terms or conditions.
[`FixedU32`]: struct.FixedU32.html
[`FixedU64`]: struct.FixedU64.html
[`FixedU8`]: struct.FixedU8.html
[`f16`]: https://docs.rs/half/^1/half/struct.f16.html
[channels]: https://doc.rust-lang.org/book/second-edition/appendix-07-nightly-rust.html
[const generics]: https://github.com/rust-lang/rust/issues/44580
*/
@ -118,6 +136,8 @@ additional terms or conditions.
#![doc(test(attr(deny(warnings))))]
#![cfg_attr(nightly_repr_transparent, feature(repr_transparent))]
#[cfg(feature = "f16")]
extern crate half;
extern crate typenum;
macro_rules! if_signed {
@ -144,12 +164,12 @@ mod helper;
use arith::MulDivDir;
use core::cmp::Ordering;
use core::f32;
use core::f64;
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use float::FloatConv;
use frac::{IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8};
#[cfg(feature = "f16")]
use half::f16;
use helper::FixedHelper;
macro_rules! pass_method {
@ -208,13 +228,13 @@ macro_rules! doc_comment_signed_unsigned {
}
macro_rules! from_float {
($Signedness:tt, fn $method:ident($Float:ident) -> $Fixed:ident < $Frac:ident >) => {
($Signedness:tt,fn $method:ident($Float:ident) -> $Fixed:ident < $Frac:ident >) => {
doc_comment_signed_unsigned! {
$Signedness,
concat!(
"Creates a fixed-point number from `", stringify!($Float), "`.\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"# Examples\n",
"\n",
@ -242,7 +262,7 @@ macro_rules! from_float {
concat!(
"Creates a fixed-point number from `", stringify!($Float), "`.\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"# Examples\n",
"\n",
@ -299,13 +319,13 @@ macro_rules! from_float {
}
macro_rules! to_float {
($Signedness:tt, fn $method:ident($Fixed:ident < $Frac:ident >) -> $Float:ident) => {
($Signedness:tt,fn $method:ident($Fixed:ident < $Frac:ident >) -> $Float:ident) => {
doc_comment_signed_unsigned! {
$Signedness,
concat!(
"Converts the fixed-point number to `", stringify!($Float), "`.\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"# Examples\n",
"\n",
@ -323,7 +343,7 @@ macro_rules! to_float {
concat!(
"Converts the fixed-point number to `", stringify!($Float), "`.\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"# Examples\n",
"\n",
@ -354,8 +374,21 @@ macro_rules! to_float {
};
}
#[cfg(feature = "f16")]
macro_rules! string_up_to_16 {
(U8, $s:expr) => {
$s
};
(U16, $s:expr) => {
$s
};
($other:tt, $s:expr) => {
""
};
}
macro_rules! fixed {
($description:expr, $Fixed:ident($Inner:ty, $Len:ty, $bits_count:expr), $Signedness:tt) => {
($description:expr, $Fixed:ident($Inner:ty, $Len:tt, $bits_count:expr), $Signedness:tt) => {
doc_comment! {
concat!(
$description,
@ -809,8 +842,180 @@ macro_rules! fixed {
}
}
#[cfg(feature = "f16")]
doc_comment_signed_unsigned! {
$Signedness,
concat!(
"Creates a fixed-point number from `f16`.\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"This method is only available when the `f16` feature is enabled.\n",
"\n",
"# Examples\n",
"\n",
"```rust\n",
"extern crate fixed;\n",
"extern crate half;\n",
"use fixed::frac;\n",
"use fixed::", stringify!($Fixed), ";\n",
"use half::f16;\n",
"type Fix = ", stringify!($Fixed), "<frac::U4>;\n",
"// 1.75 is 0001.1100, that is from_bits(28)\n",
"let val = f16::from_f32(1.75);\n",
"let neg_val = f16::from_f32(-1.75);\n",
"assert_eq!(Fix::from_f16(val), Some(Fix::from_bits(28)));\n",
"assert_eq!(Fix::from_f16(neg_val), Some(Fix::from_bits(-28)));\n",
"// 1e-2 is too small for four fractional bits\n",
"let small = f16::from_f32(1e-2);\n",
"let neg_small = f16::from_f32(-1e-2);\n",
"assert_eq!(Fix::from_f16(small), Some(Fix::from_bits(0)));\n",
"assert_eq!(Fix::from_f16(neg_small), Some(Fix::from_bits(0)));\n",
string_up_to_16!(
$Len,
concat!(
"// 50000 is too large for ", stringify!($Fixed), "<frac::U4>\n",
"let large = f16::from_f32(50000.0);\n",
"let neg_large = f16::from_f32(-50000.0);\n",
"assert!(Fix::from_f16(large).is_none());\n",
"assert!(Fix::from_f16(neg_large).is_none());\n"
)
),
"```\n"
),
concat!(
"Creates a fixed-point number from `", stringify!($Float), "`.\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"This method is only available when the `f16` feature is enabled.\n",
"\n",
"# Examples\n",
"\n",
"```rust\n",
"extern crate fixed;\n",
"extern crate half;\n",
"use fixed::frac;\n",
"use fixed::", stringify!($Fixed), ";\n",
"use half::f16;\n",
"type Fix = ", stringify!($Fixed), "<frac::U4>;\n",
"// 1.75 is 0001.1100, that is from_bits(28)\n",
"let val = f16::from_f32(1.75);\n",
"assert_eq!(Fix::from_f16(val), Some(Fix::from_bits(28)));\n",
"// 1e-2 is too small for four fractional bits\n",
"let small = f16::from_f32(1e-2);\n",
"assert_eq!(Fix::from_f16(small), Some(Fix::from_bits(0)));\n",
string_up_to_16!(
$Len,
concat!(
"// 50000 is too large for ", stringify!($Fixed), "<frac::U4>\n",
"let large = f16::from_f32(50000.0);\n",
"assert!(Fix::from_f16(large).is_none());\n",
)
),
"```\n"
),
#[inline]
pub fn from_f16(val: f16) -> Option<$Fixed<Frac>> {
let int_bits = Self::int_bits();
let frac_bits = Self::frac_bits();
let (int_frac, neg) = FloatConv::from_f16(val, frac_bits)?;
if <$Fixed<Frac> as FixedHelper<Frac>>::is_signed() {
// most significant bit (msb) can be one only for min value,
// that is for a negative value with only the msb true.
let msb = 1 << (int_bits + frac_bits - 1);
if int_frac & msb != 0 {
if !neg || (int_frac & !msb) != 0 {
return None;
}
}
} else if neg {
if int_frac != 0 {
return None;
}
return Some($Fixed::from_bits(0));
}
let (int, frac) = if frac_bits == 0 {
(int_frac, 0)
} else if int_bits == 0 {
(0, int_frac)
} else {
((int_frac >> frac_bits), (int_frac << int_bits))
};
Some(FixedHelper::from_parts(neg, int, frac))
}
}
from_float! { $Signedness, fn from_f32(f32) -> $Fixed<Frac> }
from_float! { $Signedness, fn from_f64(f64) -> $Fixed<Frac> }
#[cfg(feature = "f16")]
doc_comment_signed_unsigned! {
$Signedness,
concat!(
"Converts the fixed-point number to `f16`.\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"This method is only available when the `f16` feature is enabled.\n",
"\n",
"# Examples\n",
"\n",
"```rust\n",
"extern crate fixed;\n",
"extern crate half;\n",
"use fixed::frac;\n",
"use fixed::", stringify!($Fixed), ";\n",
"use half::f16;\n",
"type Fix = ", stringify!($Fixed), "<frac::U4>;\n",
"// 1.75 is 0001.1100, that is from_bits(28)\n",
"let val = f16::from_f32(1.75);\n",
"let neg_val = f16::from_f32(-1.75);\n",
"assert_eq!(Fix::from_bits(28).to_f16(), val);\n",
"assert_eq!(Fix::from_bits(-28).to_f16(), neg_val);\n",
"```\n"
),
concat!(
"Converts the fixed-point number to `f16`.\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"This method is only available when the `f16` feature is enabled.\n",
"\n",
"# Examples\n",
"\n",
"```rust\n",
"extern crate fixed;\n",
"extern crate half;\n",
"use fixed::frac;\n",
"use fixed::", stringify!($Fixed), ";\n",
"use half::f16;\n",
"type Fix = ", stringify!($Fixed), "<frac::U4>;\n",
"// 1.75 is 0001.1100, that is from_bits(28)\n",
"let val = f16::from_f32(1.75);\n",
"assert_eq!(Fix::from_bits(28).to_f16(), val);\n",
"```\n"
),
#[inline]
pub fn to_f16(self) -> f16 {
let int_bits = Self::int_bits();
let frac_bits = Self::frac_bits();
let (neg, int, frac) = self.parts();
let int_frac = if frac_bits == 0 {
int
} else if int_bits == 0 {
frac
} else {
(int << frac_bits) | (frac >> int_bits)
};
FloatConv::to_f16(int_frac, neg, frac_bits)
}
}
to_float! { $Signedness, fn to_f32($Fixed<Frac>) -> f32 }
to_float! { $Signedness, fn to_f64($Fixed<Frac>) -> f64 }