Skip to content

Commit 2c3739d

Browse files
authored
Merge pull request #12 from popzxc/float-convert
Numeric extensions
2 parents 6b94f49 + 8a597a8 commit 2c3739d

File tree

5 files changed

+622
-0
lines changed

5 files changed

+622
-0
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
- `try_match` and `unwrap_match` macros to get a certain variant from an enum.
66
- `return_ok` and `return_some` macros for early return of successful calculation.
7+
- `Integer` trait that unifies all the built-in integer types under a single interface.
8+
- `FloatConvert` trait that adds an interface for converting floating point numbers into integers.
79

810
## 0.2.1 (09.07.2020)
911

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
pub mod duration;
117117
#[macro_use]
118118
pub mod macros;
119+
pub mod num;
119120
pub mod option;
120121
pub mod result;
121122
pub mod str;
@@ -126,6 +127,7 @@ pub mod vec;
126127
pub mod prelude {
127128
pub use crate::{
128129
duration::*,
130+
num::{float_convert::*, integer::*},
129131
option::*,
130132
result::*,
131133
str::*,

src/num/float_convert.rs

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
//! Extensions for the built-in floating point types.
2+
3+
/// Set of methods to safely convert floating number into an integer.
4+
///
5+
/// Currently, the main way to do so is to use [`as`][as_convert] conversion.
6+
/// However, such an approach may not be suitable if saturating conversion is
7+
/// not desired.
8+
///
9+
/// However, saturating conversion is also provided as an expicit alternative
10+
/// to `as` conversion (e.g. to avoid warnings when [`clippy::as_conversions`][clippy_as]
11+
/// lint is enabled).
12+
///
13+
/// [as_convert]: https://doc.rust-lang.org/nomicon/casts.html
14+
/// [clippy_as]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
15+
///
16+
/// ## Implementors
17+
///
18+
/// This trait is implemented for both [`f32`] and [`f64`].
19+
pub trait FloatConvert<Int>: Sized {
20+
/// Floors the floating number and attempts to convert it into an integer.
21+
/// See [`f32::floor`] for description of flooring logic.
22+
///
23+
/// Returns `None` if the value will not fit into the integer range or value
24+
/// is not a number.
25+
///
26+
/// ## Examples
27+
///
28+
/// ```rust
29+
/// # use stdext::prelude::FloatConvert;
30+
///
31+
/// let valid: Option<u8> = 10.5f32.checked_floor();
32+
/// let too_big: Option<u8> = 256f32.checked_floor();
33+
/// let nan: Option<u8> = f32::NAN.checked_floor();
34+
///
35+
/// assert_eq!(valid, Some(10u8));
36+
/// assert_eq!(too_big, None);
37+
/// assert_eq!(nan, None);
38+
/// ```
39+
#[must_use = "this returns the result of the operation, without modifying the original"]
40+
fn checked_floor(self) -> Option<Int>;
41+
42+
/// Ceils the floating number and attempts to convert it into an integer.
43+
/// See [`f32::ceil`] for description of ceiling logic.
44+
///
45+
/// Returns `None` if the value will not fit into the integer range or value
46+
/// is not a number.
47+
///
48+
/// ## Examples
49+
///
50+
/// ```rust
51+
/// # use stdext::prelude::FloatConvert;
52+
///
53+
/// let valid: Option<u8> = 10.5f32.checked_ceil();
54+
/// let too_big: Option<u8> = 256f32.checked_ceil();
55+
/// let nan: Option<u8> = f32::NAN.checked_ceil();
56+
///
57+
/// assert_eq!(valid, Some(11u8));
58+
/// assert_eq!(too_big, None);
59+
/// assert_eq!(nan, None);
60+
/// ```
61+
#[must_use = "this returns the result of the operation, without modifying the original"]
62+
fn checked_ceil(self) -> Option<Int>;
63+
64+
/// Rounds the floating number and attempts to convert it into an integer.
65+
/// See [`f32::round`] for description of rounding logic.
66+
///
67+
/// Returns `None` if the value will not fit into the integer range or value
68+
/// is not a number.
69+
///
70+
/// ## Examples
71+
///
72+
/// ```rust
73+
/// # use stdext::prelude::FloatConvert;
74+
///
75+
/// let valid: Option<u8> = 10.51f32.checked_round(); // Will be rounded up.
76+
/// let too_big: Option<u8> = 256f32.checked_round();
77+
/// let nan: Option<u8> = f32::NAN.checked_round();
78+
///
79+
/// assert_eq!(valid, Some(11u8));
80+
/// assert_eq!(too_big, None);
81+
/// assert_eq!(nan, None);
82+
/// ```
83+
#[must_use = "this returns the result of the operation, without modifying the original"]
84+
fn checked_round(self) -> Option<Int>;
85+
86+
/// Behaves the same as `number.floor() as <type>`.
87+
/// See [`f32::floor`] for description of flooring logic.
88+
#[must_use = "this returns the result of the operation, without modifying the original"]
89+
fn saturated_floor(self) -> Int;
90+
91+
/// Behaves the same as `number.ceil() as <type>`.
92+
/// See [`f32::ceil`] for description of flooring logic.
93+
#[must_use = "this returns the result of the operation, without modifying the original"]
94+
fn saturated_ceil(self) -> Int;
95+
96+
/// Behaves the same as `number.round() as <type>`.
97+
/// See [`f32::round`] for description of flooring logic.
98+
#[must_use = "this returns the result of the operation, without modifying the original"]
99+
fn saturated_round(self) -> Int;
100+
}
101+
102+
macro_rules! checked_impl {
103+
($val:ident.$fn:ident(), $int:ty) => {{
104+
if $val.is_nan() || $val.is_infinite() {
105+
return None;
106+
}
107+
let converted = $val.$fn();
108+
if <$int>::MIN as Self <= converted && converted <= <$int>::MAX as Self {
109+
Some(converted as $int)
110+
} else {
111+
None
112+
}
113+
}};
114+
}
115+
116+
macro_rules! saturated_impl {
117+
($val:ident.$fn:ident(), $int:ty) => {{
118+
$val.$fn() as $int
119+
}};
120+
}
121+
122+
macro_rules! impl_float_convert {
123+
($float:ty, $($int:ty),+) => {
124+
$(impl FloatConvert<$int> for $float {
125+
fn checked_floor(self) -> Option<$int> {
126+
checked_impl!(self.floor(), $int)
127+
}
128+
129+
fn checked_ceil(self) -> Option<$int> {
130+
checked_impl!(self.ceil(), $int)
131+
}
132+
133+
fn checked_round(self) -> Option<$int> {
134+
checked_impl!(self.round(), $int)
135+
}
136+
137+
fn saturated_floor(self) -> $int {
138+
saturated_impl!(self.floor(), $int)
139+
}
140+
141+
fn saturated_ceil(self) -> $int {
142+
saturated_impl!(self.ceil(), $int)
143+
}
144+
145+
fn saturated_round(self) -> $int {
146+
saturated_impl!(self.round(), $int)
147+
}
148+
})+
149+
};
150+
}
151+
152+
impl_float_convert!(f32, u8, u16, u32, u64, u128);
153+
impl_float_convert!(f32, i8, i16, i32, i64, i128);
154+
impl_float_convert!(f32, usize, isize);
155+
156+
impl_float_convert!(f64, u8, u16, u32, u64, u128);
157+
impl_float_convert!(f64, i8, i16, i32, i64, i128);
158+
impl_float_convert!(f64, usize, isize);

0 commit comments

Comments
 (0)