From 8f64db356a43164fce21ec5e7899b6ec9782bc35 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Sat, 11 Aug 2018 17:19:17 +0200 Subject: [PATCH] add from_int, to_int*, frac and int methods --- README.md | 12 +- RELEASES.md | 3 + src/frac.rs | 31 +++ src/lib.rs | 656 +++++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 645 insertions(+), 57 deletions(-) create mode 100644 src/frac.rs diff --git a/README.md b/README.md index b763a01..4b22ea2 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,20 @@ fixed-point numbers. ### Version 0.1.1 news (unreleased) * New static methods [`int_bits`] and [`frac_bits`] were added. + * New methods [`from_int`], [`to_int`], [`to_int_ceil`], + [`to_int_floor`] and [`to_int_round`] were added. + * New methods [`int`] and [`frac`] were added. * Support for multiplication and division by integers was added. [`frac_bits`]: https://docs.rs/fixed/0.1.0/fixed/struct.FixedI32.html#method.frac_bits -[`int_bits`]: https://docs.rs/fixed/0.1.0/fixed/struct.FixedI32.html#method.int_bits +[`frac`]: https://docs.rs/fixed/0.1.0/fixed/struct.FixedI32.html#method.frac +[`from_int`]: https://docs.rs/fixed/0.1.0/fixed/struct.FixedI32.html#method.from_int +[`to_int_bits`]: https://docs.rs/fixed/0.1.0/fixed/struct.FixedI32.html#method.to_int_bits +[`to_int_ceil`]: https://docs.rs/fixed/0.1.0/fixed/struct.FixedI32.html#method.to_int_ceil +[`to_int_floor`]: https://docs.rs/fixed/0.1.0/fixed/struct.FixedI32.html#method.to_int_floor +[`to_int_round`]: https://docs.rs/fixed/0.1.0/fixed/struct.FixedI32.html#method.to_int_round +[`to_int`]: https://docs.rs/fixed/0.1.0/fixed/struct.FixedI32.html#method.to_int +[`int`]: https://docs.rs/fixed/0.1.0/fixed/struct.FixedI32.html#method.int ### Version 0.1.0 news (2018-08-10) diff --git a/RELEASES.md b/RELEASES.md index d64adac..0fc867f 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -9,6 +9,9 @@ Version 0.1.1 (unreleased) ========================== * New static methods `int_bits` and `frac_bits` were added. + * New methods `from_int`, `to_int`, `to_int_ceil`, `to_int_floor` + and `to_int_round`. + * New methods `int` and `frac` were added. * Support for multiplication and division by integers was added. Version 0.1.0 (2018-08-10) diff --git a/src/frac.rs b/src/frac.rs new file mode 100644 index 0000000..eb666fe --- /dev/null +++ b/src/frac.rs @@ -0,0 +1,31 @@ +// Copyright © 2018 Trevor Spiteri + +// This library is free software: you can redistribute it and/or +// modify it under the terms of either +// +// * the Apache License, Version 2.0 or +// * the MIT License +// +// at your option. +// +// You should have recieved copies of the Apache License and the MIT +// License along with the library. If not, see +// and +// . + +/*! +This module reexports items from the [*typenum* crate]. + +[*typenum* crate]: https://crates.io/crates/typenum +*/ + +pub use typenum::{ + U0, U1, U10, U100, U101, U102, U103, U104, U105, U106, U107, U108, U109, U11, U110, U111, U112, + U113, U114, U115, U116, U117, U118, U119, U12, U120, U121, U122, U123, U124, U125, U126, U127, + U128, U13, U14, U15, U16, U17, U18, U19, U2, U20, U21, U22, U23, U24, U25, U26, U27, U28, U29, + U3, U30, U31, U32, U33, U34, U35, U36, U37, U38, U39, U4, U40, U41, U42, U43, U44, U45, U46, + U47, U48, U49, U5, U50, U51, U52, U53, U54, U55, U56, U57, U58, U59, U6, U60, U61, U62, U63, + U64, U65, U66, U67, U68, U69, U7, U70, U71, U72, U73, U74, U75, U76, U77, U78, U79, U8, U80, + U81, U82, U83, U84, U85, U86, U87, U88, U89, U9, U90, U91, U92, U93, U94, U95, U96, U97, U98, + U99, Unsigned, +}; diff --git a/src/lib.rs b/src/lib.rs index a359e9b..8900a42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,8 +102,10 @@ additional terms or conditions. extern crate typenum; mod display; +pub mod frac; mod helper; +use frac::Unsigned; use helper::FixedHelper; use std::cmp::Ordering; use std::f32; @@ -116,7 +118,6 @@ use std::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }; -use typenum::Unsigned; macro_rules! if_signed { (Signed => $($rem:tt)+) => { @@ -328,6 +329,13 @@ macro_rules! doc_comment { }; } +macro_rules! doc_comment_signed_unsigned { + ($Signedness:tt, $signed:expr, $unsigned:expr, $($tt:tt)*) => { + if_signed! { $Signedness => doc_comment! { $signed, $($tt)* } } + if_unsigned! { $Signedness => doc_comment! { $unsigned, $($tt)* } } + }; +} + macro_rules! to_f { ($method:ident -> $f:ident($u:ident), $exp_bits:expr, $prec:expr) => { doc_comment! { @@ -400,7 +408,7 @@ macro_rules! to_f { } macro_rules! fixed { - ($description:expr, $Fixed:ident($Inner:ty), $Signedness:tt) => { + ($description:expr, $Fixed:ident($Inner:ty, $bits_count:expr), $Signedness:tt) => { doc_comment! { concat!( $description, @@ -414,16 +422,12 @@ macro_rules! fixed { "# Examples\n", "\n", "```rust\n", - "extern crate fixed;\n", - "extern crate typenum;\n", + "use fixed::frac::U3;\n", "use fixed::", stringify!($Fixed), ";\n", - "fn main() {\n", - " use typenum::U3;\n", - " let eleven = ", stringify!($Fixed), "::::from_bits(11 << 3);\n", - " let five_five = eleven >> 1u32;\n", - " assert_eq!(eleven.to_string(), \"11.0\");\n", - " assert_eq!(five_five.to_string(), \"5.5\");\n", - "}\n", + "let eleven = ", stringify!($Fixed), "::::from_bits(11 << 3);\n", + "let five_half = eleven >> 1u32;\n", + "assert_eq!(eleven.to_string(), \"11.0\");\n", + "assert_eq!(five_half.to_string(), \"5.5\");\n", "```\n", "\n", "[`Unsigned`]: https://docs.rs/typenum/^1.3/typenum/marker_traits/trait.Unsigned.html\n", @@ -499,6 +503,394 @@ macro_rules! fixed { Frac::to_u32() } + doc_comment! { + concat!( + "Creates a fixed-point number of type `", stringify!($Fixed), "`\n", + "that has a bitwise representation identical to the\n", + "`", stringify!($Inner), "` value." + ), + #[inline] + pub fn from_bits(v: $Inner) -> $Fixed { + let bits = <$Fixed as FixedHelper>::bits(); + assert!(Frac::to_u32() <= bits, "`Frac` too large"); + $Fixed((v, PhantomData)) + } + } + + doc_comment! { + concat!( + "Creates an integer of type `", stringify!($Inner), "`\n", + "that has a bitwise representation identical to the\n", + "`", stringify!($Fixed), "` value." + ), + #[inline] + pub fn to_bits(self) -> $Inner { + (self.0).0 + } + } + + doc_comment! { + concat!( + "Creates a fixed-point number of type `", stringify!($Fixed), "`\n", + "that has the same value as an integer of type\n", + "`", stringify!($Inner), "` if it fits.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "let fix_one = Fix::from_bits(1 << 4);\n", + "assert_eq!(Fix::from_int(1), Some(fix_one));\n", + "let too_large = 1 << (", stringify!($bits_count), " - 2);\n", + "assert_eq!(Fix::from_int(too_large), None);\n", + "```\n" + ), + #[inline] + pub fn from_int(v: $Inner) -> Option<$Fixed> { + let frac_bits = <$Fixed>::frac_bits(); + let bits = v.checked_shl(frac_bits).unwrap_or(0); + let all_frac_check; + if_signed! { $Signedness => all_frac_check = bits >> (frac_bits - 1); } + if_unsigned! { $Signedness => all_frac_check = 0; } + + let check = bits.checked_shr(frac_bits).unwrap_or(all_frac_check); + if check == v { + Some($Fixed::from_bits(bits)) + } else { + None + } + } + } + + doc_comment_signed_unsigned! { + $Signedness, + concat!( + "Converts the fixed-point number of type `", stringify!($Fixed), "`\n", + "to an integer of type\n", + "`", stringify!($Inner), "` truncating the fractional bits.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "let two_half = Fix::from_int(5).unwrap() / 2;\n", + "assert_eq!(two_half.to_int(), 2);\n", + "let neg_two_half = -two_half;\n", + "assert_eq!(neg_two_half.to_int(), -2);\n", + "```\n" + ), + concat!( + "Converts the fixed-point number of type `", stringify!($Fixed), "`\n", + "to an integer of type\n", + "`", stringify!($Inner), "` truncating the fractional bits.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "let two_half = Fix::from_int(5).unwrap() / 2;\n", + "assert_eq!(two_half.to_int(), 2);\n", + "```\n" + ), + #[inline] + pub fn to_int(self) -> $Inner { + let floor = self.to_int_floor(); + if_signed! { $Signedness => { + let no_frac = self.frac().to_bits() == 0; + if no_frac || self.to_bits() >= 0 { + floor + } else { + floor + 1 + } + } } + if_unsigned! { $Signedness => { + floor + } } + } + } + + doc_comment_signed_unsigned! { + $Signedness, + concat!( + "Converts the fixed-point number of type `", stringify!($Fixed), "`\n", + "to an integer of type\n", + "`", stringify!($Inner), "` rounding towards +∞.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "let two_half = Fix::from_int(5).unwrap() / 2;\n", + "assert_eq!(two_half.to_int_ceil(), 3);\n", + "let neg_two_half = -two_half;\n", + "assert_eq!(neg_two_half.to_int_ceil(), -2);\n", + "```\n" + ), + concat!( + "Converts the fixed-point number of type `", stringify!($Fixed), "`\n", + "to an integer of type\n", + "`", stringify!($Inner), "` rounding towards +∞.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "let two_half = Fix::from_int(5).unwrap() / 2;\n", + "assert_eq!(two_half.to_int_ceil(), 3);\n", + "```\n" + ), + #[inline] + pub fn to_int_ceil(self) -> $Inner { + let floor = self.to_int_floor(); + let no_frac = self.frac().to_bits() == 0; + if no_frac { + floor + } else { + floor + 1 + } + } + } + + doc_comment_signed_unsigned! { + $Signedness, + concat!( + "Converts the fixed-point number of type `", stringify!($Fixed), "`\n", + "to an integer of type\n", + "`", stringify!($Inner), "` rounding towards −∞.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "let two_half = Fix::from_int(5).unwrap() / 2;\n", + "assert_eq!(two_half.to_int_floor(), 2);\n", + "let neg_two_half = -two_half;\n", + "assert_eq!(neg_two_half.to_int_floor(), -3);\n", + "```\n" + ), + concat!( + "Converts the fixed-point number of type `", stringify!($Fixed), "`\n", + "to an integer of type\n", + "`", stringify!($Inner), "` rounding towards −∞.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "let two_half = Fix::from_int(5).unwrap() / 2;\n", + "assert_eq!(two_half.to_int_floor(), 2);\n", + "```\n" + ), + #[inline] + pub fn to_int_floor(self) -> $Inner { + let bits = self.to_bits(); + if Self::int_bits() == 0 { + if_signed! { $Signedness => bits >> (Self::frac_bits() - 1) } + if_unsigned! { $Signedness => 0 } + } else { + bits >> Self::frac_bits() + } + } + } + + doc_comment_signed_unsigned! { + $Signedness, + concat!( + "Converts the fixed-point number of type `", stringify!($Fixed), "`\n", + "to an integer of type\n", + "`", stringify!($Inner), "` rounding towards the nearest.\n", + "Ties are rounded away from zero.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "let two_half = Fix::from_int(5).unwrap() / 2;\n", + "assert_eq!(two_half.to_int_round(), 3);\n", + "let neg_two_half = -two_half;\n", + "assert_eq!(neg_two_half.to_int_round(), -3);\n", + "let one_quarter = two_half / 2;\n", + "assert_eq!(one_quarter.to_int_round(), 1);\n", + "```\n" + ), + concat!( + "Converts the fixed-point number of type `", stringify!($Fixed), "`\n", + "to an integer of type\n", + "`", stringify!($Inner), "` rounding towards −∞.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "let two_half = Fix::from_int(5).unwrap() / 2;\n", + "assert_eq!(two_half.to_int_round(), 3);\n", + "let one_quarter = two_half / 2;\n", + "assert_eq!(one_quarter.to_int_round(), 1);\n", + "```\n" + ), + #[inline] + pub fn to_int_round(self) -> $Inner { + let frac_bits = <$Fixed>::frac_bits(); + let floor = self.to_int_floor(); + if frac_bits == 0 { + return floor; + } + let half_bit = 1 << (frac_bits - 1); + if_signed! { $Signedness => { + if self.to_bits() >= 0 { + if (self.to_bits() & half_bit) != 0 { + floor + 1 + } else { + floor + } + } else { + let neg = self.to_bits().wrapping_neg(); + if (neg & half_bit) != 0 { + floor + } else { + floor + 1 + } + } + } } + if_unsigned! { $Signedness => { + if (self.to_bits() & half_bit) != 0 { + floor + 1 + } else { + floor + } + } } + } + } + + doc_comment_signed_unsigned! { + $Signedness, + concat!( + "Returns the integer part.\n", + "\n", + "Note that since the numbers are stored in two’s\n", + "complement, negative numbers with non-zero fractional\n", + "parts will be rounded towards −∞, except in the case\n", + "where there are no integer bits, that is `", + stringify!($Fixed), "`,\n", + "where the return value is always zero.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "// 0010.0000\n", + "let two = Fix::from_int(2).unwrap();\n", + "// 0010.0100\n", + "let two_and_quarter = two + two / 8;\n", + "assert_eq!(two_and_quarter.int(), two);\n", + "// 1101.0000\n", + "let neg_three = Fix::from_int(-3).unwrap();\n", + "// 1101.1100\n", + "let neg_two_and_quarter = -two_and_quarter;\n", + "assert_eq!(neg_two_and_quarter.int(), neg_three);\n", + "```\n" + ), + concat!( + "Returns the integer part.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "// 0010.0000\n", + "let two = Fix::from_int(2).unwrap();\n", + "// 0010.0100\n", + "let two_and_quarter = two + two / 8;\n", + "assert_eq!(two_and_quarter.int(), two);\n", + "```\n" + ), + #[inline] + pub fn int(self) -> $Fixed { + let frac_bits = <$Fixed>::frac_bits(); + let mask = <$Inner>::checked_shl(!0, frac_bits).unwrap_or(0); + $Fixed::from_bits(self.to_bits() & mask) + } + } + + doc_comment_signed_unsigned! { + $Signedness, + concat!( + "Returns the fractional part.\n", + "\n", + "Note that since the numbers are stored in two’s\n", + "complement, the returned fraction will be non-negative\n", + "for negative numbers, except in the case where\n", + "there are no integer bits, that is `", + stringify!($Fixed), "`,\n", + "where the return value is always equal to `self`.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "// 0000.0100\n", + "let quarter = Fix::from_int(1).unwrap() / 4;\n", + "// 0010.0100\n", + "let two_and_quarter = quarter * 9;\n", + "assert_eq!(two_and_quarter.frac(), quarter);\n", + "// 0000.1100\n", + "let three_quarters = quarter * 3;\n", + "// 1101.1100\n", + "let neg_two_and_quarter = -two_and_quarter;\n", + "assert_eq!(neg_two_and_quarter.frac(), three_quarters);\n", + "```\n" + ), + concat!( + "Returns the fractional part.\n", + "\n", + "# Examples\n", + "\n", + "```rust\n", + "use fixed::frac;\n", + "use fixed::", stringify!($Fixed), ";\n", + "type Fix = ", stringify!($Fixed), ";\n", + "// 0000.0100\n", + "let quarter = Fix::from_int(1).unwrap() / 4;\n", + "// 0010.0100\n", + "let two_and_quarter = quarter * 9;\n", + "assert_eq!(two_and_quarter.frac(), quarter);\n", + "```\n" + ), + #[inline] + pub fn frac(self) -> $Fixed { + let frac_bits = <$Fixed>::frac_bits(); + let inv_mask = <$Inner>::checked_shl(!0, frac_bits).unwrap_or(0); + $Fixed::from_bits(self.to_bits() & !inv_mask) + } + } + + to_f! { to_f32 -> f32(u32), 8, 24 } + to_f! { to_f64 -> f64(u64), 11, 53 } + pass_method! { "Returns the number of ones in the binary representation.", $Fixed($Inner) => fn count_ones(self) -> u32 @@ -843,35 +1235,6 @@ macro_rules! fixed { } } } - - doc_comment! { - concat!( - "Creates a fixed-point number of type `", stringify!($Fixed), "`\n", - "that has a bitwise representation identical to the\n", - "`", stringify!($Inner), "` value." - ), - #[inline] - pub fn from_bits(v: $Inner) -> $Fixed { - let bits = <$Fixed as FixedHelper>::bits(); - assert!(Frac::to_u32() <= bits, "`Frac` too large"); - $Fixed((v, PhantomData)) - } - } - - doc_comment! { - concat!( - "Creates an integer of type `", stringify!($Inner), "`\n", - "that has a bitwise representation identical to the\n", - "`", stringify!($Fixed), "` value." - ), - #[inline] - pub fn to_bits(self) -> $Inner { - (self.0).0 - } - } - - to_f! { to_f32 -> f32(u32), 8, 24 } - to_f! { to_f64 -> f64(u64), 11, 53 } } if_signed! { @@ -1148,16 +1511,16 @@ macro_rules! fixed { }; } -fixed! { "An eight-bit fixed-point unsigned integer", FixedU8(u8), Unsigned } -fixed! { "A 16-bit fixed-point unsigned integer", FixedU16(u16), Unsigned } -fixed! { "A 32-bit fixed-point unsigned integer", FixedU32(u32), Unsigned } -fixed! { "A 64-bit fixed-point unsigned integer", FixedU64(u64), Unsigned } -fixed! { "A 128-bit fixed-point unsigned integer", FixedU128(u128), Unsigned } -fixed! { "An eight-bit fixed-point signed integer", FixedI8(i8), Signed } -fixed! { "A 16-bit fixed-point signed integer", FixedI16(i16), Signed } -fixed! { "A 32-bit fixed-point signed integer", FixedI32(i32), Signed } -fixed! { "A 64-bit fixed-point signed integer", FixedI64(i64), Signed } -fixed! { "A 128-bit fixed-point signed integer", FixedI128(i128), Signed } +fixed! { "An eight-bit fixed-point unsigned integer", FixedU8(u8, 8), Unsigned } +fixed! { "A 16-bit fixed-point unsigned integer", FixedU16(u16, 16), Unsigned } +fixed! { "A 32-bit fixed-point unsigned integer", FixedU32(u32, 32), Unsigned } +fixed! { "A 64-bit fixed-point unsigned integer", FixedU64(u64, 64), Unsigned } +fixed! { "A 128-bit fixed-point unsigned integer", FixedU128(u128, 128), Unsigned } +fixed! { "An eight-bit fixed-point signed integer", FixedI8(i8, 8), Signed } +fixed! { "A 16-bit fixed-point signed integer", FixedI16(i16, 16), Signed } +fixed! { "A 32-bit fixed-point signed integer", FixedI32(i32, 32), Signed } +fixed! { "A 64-bit fixed-point signed integer", FixedI64(i64, 64), Signed } +fixed! { "A 128-bit fixed-point signed integer", FixedI128(i128, 128), Signed } trait MulDivDir: Sized { fn mul_dir(self, rhs: Self, frac_bits: u32) -> (Self, Ordering); @@ -1396,7 +1759,7 @@ mod tests { #[test] fn fixed_u16() { - use typenum::U7 as Frac; + use frac::U7 as Frac; let frac = Frac::to_u32(); let a = 12; let b = 4; @@ -1416,7 +1779,7 @@ mod tests { #[test] fn fixed_i16() { - use typenum::U7 as Frac; + use frac::U7 as Frac; let frac = Frac::to_u32(); let a = 12; let b = 4; @@ -1440,7 +1803,7 @@ mod tests { #[test] fn fixed_u128() { - use typenum::U7 as Frac; + use frac::U7 as Frac; let frac = Frac::to_u32(); let a = 0x0003456789abcdef_0123456789abcdef_u128; let b = 5; @@ -1458,7 +1821,7 @@ mod tests { #[test] fn fixed_i128() { - use typenum::U7 as Frac; + use frac::U7 as Frac; let frac = Frac::to_u32(); let a = 0x0003456789abcdef_0123456789abcdef_i128; let b = 5; @@ -1479,7 +1842,7 @@ mod tests { #[test] fn to_f32() { - use typenum::U7 as Frac; + use frac::U7 as Frac; for u in 0x00..=0xff { let fu = FixedU8::::from_bits(u); assert_eq!(fu.to_f32(), u as f32 / 128.0); @@ -1525,7 +1888,7 @@ mod tests { #[test] fn to_f64() { - use typenum::U7 as Frac; + use frac::U7 as Frac; for u in 0x00..=0xff { let fu = FixedU8::::from_bits(u); assert_eq!(fu.to_f32(), u as f32 / 128.0); @@ -1568,4 +1931,185 @@ mod tests { } } } + + #[test] + fn rounding() { + use typenum::{U16, U32}; + + type I0F32 = FixedI32; + + // -0.5 + let f = I0F32::from_bits(-1 << 31); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 0); + assert_eq!(f.to_int_floor(), -1); + assert_eq!(f.to_int_round(), -1); + + // -0.5 + δ + let f = I0F32::from_bits((-1 << 31) + 1); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 0); + assert_eq!(f.to_int_floor(), -1); + assert_eq!(f.to_int_round(), 0); + + // 0.5 - δ + let f = I0F32::from_bits((1 << 30) - 1 + (1 << 30)); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 1); + assert_eq!(f.to_int_floor(), 0); + assert_eq!(f.to_int_round(), 0); + + type U0F32 = FixedU32; + + // 0.5 - δ + let f = U0F32::from_bits((1 << 31) - 1); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 1); + assert_eq!(f.to_int_floor(), 0); + assert_eq!(f.to_int_round(), 0); + + // 0.5 + let f = U0F32::from_bits(1 << 31); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 1); + assert_eq!(f.to_int_floor(), 0); + assert_eq!(f.to_int_round(), 1); + + // 0.5 + δ + let f = U0F32::from_bits((1 << 31) + 1); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 1); + assert_eq!(f.to_int_floor(), 0); + assert_eq!(f.to_int_round(), 1); + + type I16F16 = FixedI32; + + // -3.5 - δ + let f = I16F16::from_bits(((-7) << 15) - 1); + assert_eq!(f.to_int(), -3); + assert_eq!(f.to_int_ceil(), -3); + assert_eq!(f.to_int_floor(), -4); + assert_eq!(f.to_int_round(), -4); + + // -3.5 + let f = I16F16::from_bits((-7) << 15); + assert_eq!(f.to_int(), -3); + assert_eq!(f.to_int_ceil(), -3); + assert_eq!(f.to_int_floor(), -4); + assert_eq!(f.to_int_round(), -4); + + // -3.5 + δ + let f = I16F16::from_bits(((-7) << 15) + 1); + assert_eq!(f.to_int(), -3); + assert_eq!(f.to_int_ceil(), -3); + assert_eq!(f.to_int_floor(), -4); + assert_eq!(f.to_int_round(), -3); + + // -0.5 - δ + let f = I16F16::from_bits(((-1) << 15) - 1); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 0); + assert_eq!(f.to_int_floor(), -1); + assert_eq!(f.to_int_round(), -1); + + // -0.5 + let f = I16F16::from_bits((-1) << 15); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 0); + assert_eq!(f.to_int_floor(), -1); + assert_eq!(f.to_int_round(), -1); + + // -0.5 + δ + let f = I16F16::from_bits(((-1) << 15) + 1); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 0); + assert_eq!(f.to_int_floor(), -1); + assert_eq!(f.to_int_round(), 0); + + // 0.5 - δ + let f = I16F16::from_bits((1 << 15) - 1); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 1); + assert_eq!(f.to_int_floor(), 0); + assert_eq!(f.to_int_round(), 0); + + // 0.5 + let f = I16F16::from_bits(1 << 15); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 1); + assert_eq!(f.to_int_floor(), 0); + assert_eq!(f.to_int_round(), 1); + + // 0.5 + δ + let f = I16F16::from_bits((1 << 15) + 1); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 1); + assert_eq!(f.to_int_floor(), 0); + assert_eq!(f.to_int_round(), 1); + + // 3.5 - δ + let f = I16F16::from_bits((7 << 15) - 1); + assert_eq!(f.to_int(), 3); + assert_eq!(f.to_int_ceil(), 4); + assert_eq!(f.to_int_floor(), 3); + assert_eq!(f.to_int_round(), 3); + + // 3.5 + let f = I16F16::from_bits(7 << 15); + assert_eq!(f.to_int(), 3); + assert_eq!(f.to_int_ceil(), 4); + assert_eq!(f.to_int_floor(), 3); + assert_eq!(f.to_int_round(), 4); + + // 3.5 + δ + let f = I16F16::from_bits((7 << 15) + 1); + assert_eq!(f.to_int(), 3); + assert_eq!(f.to_int_ceil(), 4); + assert_eq!(f.to_int_floor(), 3); + assert_eq!(f.to_int_round(), 4); + + type U16F16 = FixedU32; + + // 0.5 - δ + let f = U16F16::from_bits((1 << 15) - 1); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 1); + assert_eq!(f.to_int_floor(), 0); + assert_eq!(f.to_int_round(), 0); + + // 0.5 + let f = U16F16::from_bits(1 << 15); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 1); + assert_eq!(f.to_int_floor(), 0); + assert_eq!(f.to_int_round(), 1); + + // 0.5 + δ + let f = U16F16::from_bits((1 << 15) + 1); + assert_eq!(f.to_int(), 0); + assert_eq!(f.to_int_ceil(), 1); + assert_eq!(f.to_int_floor(), 0); + assert_eq!(f.to_int_round(), 1); + + // 3.5 - δ + let f = U16F16::from_bits((7 << 15) - 1); + assert_eq!(f.to_int(), 3); + assert_eq!(f.to_int_ceil(), 4); + assert_eq!(f.to_int_floor(), 3); + assert_eq!(f.to_int_round(), 3); + + // 3.5 + let f = U16F16::from_bits(7 << 15); + assert_eq!(f.to_int(), 3); + assert_eq!(f.to_int_ceil(), 4); + assert_eq!(f.to_int_floor(), 3); + assert_eq!(f.to_int_round(), 4); + + // 3.5 + δ + let f = U16F16::from_bits((7 << 15) + 1); + assert_eq!(f.to_int(), 3); + assert_eq!(f.to_int_ceil(), 4); + assert_eq!(f.to_int_floor(), 3); + assert_eq!(f.to_int_round(), 4); + } }