close_to/
lib.rs

1use std::fmt::Display;
2
3use num::Float;
4
5/// Determine if two decimals are equal with the specified precision.
6///
7/// 2つの小数が指定した精度で等しいかどうかを判定する。
8///
9/// # Examples
10///
11/// ```
12/// use close_to::close_to;
13///
14/// let (is_close, expected_diff, received_diff) = close_to(1.0, 1.0001, 3);
15/// assert!(is_close);
16///```
17/// ```should_panic
18/// use close_to::close_to;
19///
20/// let (is_close, expected_diff, received_diff) = close_to(1.0, 1.0001, 4);
21/// assert!(is_close);
22/// ```
23/// ```
24pub fn close_to<T: Float, U: Into<T>>(left: T, right: U, precision: i32) -> (bool, T, T)
25{
26    let expected_diff = T::from(10).unwrap().powi(-precision) / T::from(2).unwrap();
27
28    let received_diff = (left - right.into()).abs();
29
30    (received_diff <= expected_diff, expected_diff, received_diff)
31}
32
33/// Ensures that two decimals are equal with the specified precision.
34/// In case of panic, the function outputs the value of the decimal along with a debug expression.
35///
36/// 2つの小数が指定した精度で等しいことを保証する。
37/// パニックになると、この関数は小数の値をデバッグ表現とともに出力する。
38///
39/// # Examples
40///
41/// ```
42/// use close_to::assert_close_to;
43///
44/// assert_close_to(1.0, 1.0001, 3);
45/// ```
46/// ```should_panic
47/// use close_to::assert_close_to;
48///
49/// assert_close_to(1.0, 1.0001, 4); // panic with the following message
50///
51/// // assertion 'left ≈ right` failed
52/// // left:  1
53/// // right: 1.0001
54/// // received_diff: 0.00009999999999998899
55/// // expected_diff: 0.00005
56/// ```
57/// ```
58pub fn assert_close_to<T: Float + Display + Copy, U: Display + Into<T> + Copy>(left: T, right: U, precision: i32)
59{
60    let (is_close, expected_diff, received_diff) = close_to(left, right, precision);
61
62    if !is_close
63    {
64        panic!("assertion 'left ≈ right` failed\n left:  {}\nright: {}\nreceived_diff: {}\nexpected_diff: {}", left, right, received_diff, expected_diff);
65    }
66}
67
68/// Alias for `assert_close_to` for compatibility with [assert-be-close](https://crates.io/crates/assert-be-close).
69///
70/// [assert-be-close](https://crates.io/crates/assert-be-close)との互換性を保つための`assert_close_to`のエイリアス。
71pub fn assert_be_close<T: Float + Display + Copy, U: Display + Into<T> + Copy>(left: T, right: U, precision: i32)
72{
73    assert_close_to(left, right, precision);
74}
75
76/// Determine if two decimals are not equal with the specified precision.
77///
78/// 2つの小数が指定した精度で等しくないかどうかを判定する。
79///
80/// # Examples
81///
82/// ```
83/// use close_to::far_from;
84///
85/// let (is_far, expected_diff, received_diff) = far_from(1.0, 1.0001, 4);
86/// assert!(is_far);
87/// ```
88/// ```should_panic
89/// use close_to::far_from;
90///
91/// let (is_far, expected_diff, received_diff) = far_from(1.0, 1.0001, 3);
92/// assert!(is_far);
93/// ```
94pub fn far_from<T: Float, U: Into<T>>(left: T, right: U, precision: i32) -> (bool, T, T)
95{
96    let (is_close, expected_diff, received_diff) = close_to(left, right, precision);
97
98    (!is_close, expected_diff, received_diff)
99}
100
101/// Ensures that two decimals are not equal with the specified precision.
102/// In case of panic, the function outputs the value of the decimal along with a debugging expression.
103///
104/// 2つの小数が指定した精度で等しくないことを保証する。
105/// パニックになると、この関数は小数の値をデバッグ表現とともに出力する。
106///
107/// # Examples
108///
109/// ```
110/// use close_to::assert_far_from;
111///
112/// assert_far_from(1.0, 1.0001, 4);
113/// ```
114/// ```should_panic
115/// use close_to::assert_far_from;
116///
117/// assert_far_from(1.0, 1.0001, 3); // panic with the following message
118///
119/// // assertion 'left != right` failed
120/// // left:  1
121/// // right: 1.0001
122/// // received_diff: 0.00009999999999998899
123/// // expected_diff: 0.0005
124/// ```
125pub fn assert_far_from<T: Float + Display + Copy, U: Display + Into<T> + Copy>(left: T, right: U, precision: i32)
126{
127    let (is_far, expected_diff, received_diff) = far_from(left, right, precision);
128
129    if !is_far
130    {
131        panic!("assertion 'left != right` failed\n left:  {}\nright: {}\nreceived_diff: {}\nexpected_diff: {}", left, right, received_diff, expected_diff);
132    }
133}
134
135/// Alias for `assert_not_close` for compatibility with [assert-not-close](https://crates.io/crates/assert-not-close).
136///
137/// [assert-not-close](https://crates.io/crates/assert-not-close)との互換性を保つための`assert_not_close`のエイリアス。
138pub fn assert_not_close<T: Float + Display + Copy, U: Display + Into<T> + Copy>(left: T, right: U, precision: i32)
139{
140    assert_far_from(left, right, precision);
141}
142
143/// Trait to determine if two decimals are equal to a specified precision.
144/// Implemented in float type by default.
145///
146/// 2つの小数が指定した精度で等しいかどうかを判定するトレイト。
147/// デフォルトでfloat型に実装されている。
148///
149/// # Examples
150///
151/// ```
152/// use close_to::CloseTo;
153///
154/// let (is_close, ..) = 1.0.close_to(1.0001, 3);
155/// assert!(is_close);
156///
157/// assert!(1.0.far_from(1.0001, 4).0);
158/// ```
159pub trait CloseTo<T, U: Into<T>>
160{
161    /// Determine if two decimals are equal with the specified precision.
162    ///
163    /// 2つの小数が指定した精度で等しいかどうかを判定する。
164    fn close_to(&self, other: U, precision: i32) -> (bool, T, T);
165
166    /// Determine if two decimals are not equal with the specified precision.
167    ///
168    /// 2つの小数が指定した精度で等しくないかどうかを判定する。
169    fn far_from(&self, other: U, precision: i32) -> (bool, T, T);
170}
171
172/// A trace that guarantees that two decimals are equal to a specified precision.
173/// Implemented in float type by default.
174///
175/// 2つの小数が指定した精度で等しいことを保証するトレイト。
176/// デフォルトでfloat型に実装されている。
177///
178/// # Examples
179///
180/// ```
181/// use close_to::{assert_far_from, AssertCloseTo};
182///
183/// 1.0.assert_close_to(1.0001, 3);
184/// 1.0.assert_far_from(1.0001, 4);
185/// ```
186pub trait AssertCloseTo<T>
187{
188    /// Ensures that two decimals are equal with the specified precision.
189    ///
190    /// 2つの小数が指定した精度で等しいことを保証する。
191    fn assert_close_to(&self, other: T, precision: i32);
192    /// Ensures that no two decimals are equal at the specified precision.
193    ///
194    /// 2つの小数が指定した精度で等しくないことを保証する。
195    fn assert_far_from(&self, other: T, precision: i32);
196}
197
198impl<T: Float, U: Into<T>> CloseTo<T, U> for T
199{
200    fn close_to(&self, other: U, precision: i32) -> (bool, T, T)
201    {
202        close_to(*self, other, precision)
203    }
204
205    fn far_from(&self, other: U, precision: i32) -> (bool, T, T)
206    {
207        far_from(*self, other, precision)
208    }
209}
210
211impl<T: Float + Display, U: Into<T> + Display + Copy> AssertCloseTo<U> for T
212{
213    fn assert_close_to(&self, other: U, precision: i32)
214    {
215        assert_close_to(*self, other, precision)
216    }
217
218    fn assert_far_from(&self, other: U, precision: i32)
219    {
220        assert_far_from(*self, other, precision)
221    }
222}
223
224#[cfg(test)]
225mod test
226{
227    use super::*;
228
229    #[test]
230    fn test_close_to()
231    {
232        let (is_close, ..) = close_to(1.0, 1.0001, 3);
233        assert!(is_close);
234
235        let (is_close, ..) = close_to(1.0, 1.0001, 4);
236        assert!(!is_close);
237    }
238
239    #[test]
240    fn test_assert_close_to()
241    {
242        assert_close_to(1.0, 1.0001, 3);
243        assert_far_from(1.0, 1.0001, 4);
244    }
245
246    #[test]
247    #[should_panic]
248    fn test_assert_close_to_panic()
249    {
250        assert_close_to(1.0, 1.0001, 4);
251    }
252
253    #[test]
254    #[should_panic]
255    fn test_assert_far_from_panic()
256    {
257        assert_far_from(1.0, 1.0001, 3);
258    }
259
260    #[test]
261    fn test_trait()
262    {
263        let (is_close, ..) = 1.0.close_to(1.0001, 3);
264        assert!(is_close);
265
266        let (is_close, ..) = 1.0.close_to(1.0001, 4);
267        assert!(!is_close);
268    }
269
270    #[test]
271    fn test_trait_assert()
272    {
273        1.0.assert_close_to(1.0001, 3);
274        1.0.assert_far_from(1.0001, 4);
275    }
276
277    #[test]
278    #[should_panic]
279    fn test_trait_assert_panic()
280    {
281        1.0.assert_close_to(1.0001, 4);
282    }
283
284    #[test]
285    #[should_panic]
286    fn test_trait_assert_far_from_panic()
287    {
288        1.0.assert_far_from(1.0001, 3);
289    }
290}