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}