si_scale/
value.rs

1//! Represents a float value using its mantissa and unit Prefix in a base.
2//!
3//! With base = 1000, 1k = 1000, 1M = 1_000_000, 1m = 0.001, 1µ = 0.000_001,
4//! etc.
5//!
6//! | min (incl.) | max (excl.)      | magnitude | prefix          |
7//! | ---         | ---              | ---       | ----            |
8//! | ..          | ..               | -24       | `Prefix::Yocto` |
9//! | ..          | ..               | -21       | `Prefix::Zepto` |
10//! | ..          | ..               | -18       | `Prefix::Atto`  |
11//! | ..          | ..               | -15       | `Prefix::Femto` |
12//! | ..          | ..               | -12       | `Prefix::Pico`  |
13//! | ..          | ..               | -9        | `Prefix::Nano`  |
14//! | 0.000\_001  | 0.001            | -6        | `Prefix::Micro` |
15//! | 0.001       | 1                | -3        | `Prefix::Milli` |
16//! | 1           | 1_000            | 0         | `Prefix::Unit`  |
17//! | 1000        | 1\_000\_000      | 3         | `Prefix::Kilo`  |
18//! | 1\_000\_000 | 1\_000\_000\_000 | 6         | `Prefix::Mega`  |
19//! | ..          | ..               | 9         | `Prefix::Giga`  |
20//! | ..          | ..               | 12        | `Prefix::Tera`  |
21//! | ..          | ..               | 15        | `Prefix::Peta`  |
22//! | ..          | ..               | 18        | `Prefix::Exa`   |
23//! | ..          | ..               | 21        | `Prefix::Zetta` |
24//! | ..          | ..               | 24        | `Prefix::Yotta` |
25//!
26//! The base is usually 1000, but can also be 1024 (bibytes).
27//!
28//! With base = 1024, 1ki = 1024, 1Mi = 1024 * 1024, etc.
29//!
30//! # Example
31//!
32//! ```
33//! use std::convert::From;
34//! use si_scale::{base::Base, value::Value, prefix::Prefix};
35//!
36//! let actual = Value::from(0.123);
37//! let expected = Value {
38//!     mantissa: 123f64,
39//!     prefix: Prefix::Milli,
40//!     base: Base::B1000,
41//! };
42//! assert_eq!(actual, expected);
43//! ```
44
45use crate::prefix::Prefix;
46use std::convert::From;
47use std::fmt;
48
49use crate::base::Base;
50use crate::prefix::Constraint;
51
52/// A trait for types that can be converted to `f64`.
53///
54/// This trait enables uniform handling of all numeric types, including those
55/// like `u64`, `i64`, `usize`, and `isize` that don't implement `Into<f64>`
56/// because the conversion may be lossy.
57#[diagnostic::on_unimplemented(
58    message = "`{Self}` cannot be converted to `f64`",
59    label = "this type doesn't implement `IntoF64`",
60    note = "for `u64`, `i64`, `usize`, or `isize`, enable the `lossy-conversions` feature"
61)]
62pub trait IntoF64 {
63    /// Converts self to `f64`.
64    fn into_f64(self) -> f64;
65}
66
67macro_rules! impl_into_f64_lossless {
68    ($($t:ty),*) => {
69        $(
70            impl IntoF64 for $t {
71                #[inline]
72                fn into_f64(self) -> f64 {
73                    self.into()
74                }
75            }
76        )*
77    };
78}
79
80impl_into_f64_lossless!(u8, i8, u16, i16, u32, i32, f32, f64);
81
82#[cfg(feature = "lossy-conversions")]
83macro_rules! impl_into_f64_lossy {
84    ($($t:ty),*) => {
85        $(
86            impl IntoF64 for $t {
87                #[inline]
88                fn into_f64(self) -> f64 {
89                    self as f64
90                }
91            }
92        )*
93    };
94}
95
96#[cfg(feature = "lossy-conversions")]
97impl_into_f64_lossy!(u64, i64, usize, isize);
98
99/// Defines the representation of the value.
100#[derive(Debug, PartialEq)]
101pub struct Value {
102    /// Mantissa of the value after scaling.
103    pub mantissa: f64,
104
105    /// Prefix indicating the scale.
106    pub prefix: Prefix,
107
108    /// Indicates if the base is `1000` or `1024`.
109    pub base: Base,
110}
111
112impl Value {
113    /// Returns a `Value` for the default base `B1000`, meaning `1 k = 1000`,
114    /// `1 m = 1e-3`, etc.
115    ///
116    /// # Example
117    ///
118    /// ```
119    /// use si_scale::prelude::{Base, Prefix, Value};
120    ///
121    /// let actual = Value::new(-4.6e-5);
122    /// let expected = Value {
123    ///     mantissa: -46f64,
124    ///     prefix: Prefix::Micro,
125    ///     base: Base::B1000,
126    /// };
127    /// assert_eq!(actual, expected);
128    /// ```
129    ///
130    /// # Note
131    ///
132    /// As always the case in floating point operations, you may encounter
133    /// approximate representations. For instance:
134    ///
135    /// ```
136    /// use si_scale::prelude::{Base, Prefix, Value};
137    ///
138    /// let actual = Value::new(-4.3e-5);
139    /// let expected = Value {
140    ///     mantissa: -43.00000000000001f64,
141    ///     prefix: Prefix::Micro,
142    ///     base: Base::B1000,
143    /// };
144    /// assert_eq!(actual, expected);
145    /// ```
146    ///
147    pub fn new<F>(x: F) -> Self
148    where
149        F: IntoF64,
150    {
151        Value::new_with(x, Base::B1000, Constraint::None)
152    }
153
154    /// Returns a `Value` for the provided base.
155    ///
156    /// # Example
157    ///
158    /// ```
159    /// use si_scale::prelude::{Constraint, Base, Prefix, Value};
160    ///
161    /// // 4 MiB
162    /// let actual = Value::new_with(4 * 1024 * 1024, Base::B1024, Constraint::None);
163    /// let expected = Value {
164    ///     mantissa: 4f64,
165    ///     prefix: Prefix::Mega,
166    ///     base: Base::B1024,
167    /// };
168    /// assert_eq!(actual, expected);
169    /// ```
170    ///
171    // #[deprecated(
172    //     since = "0.2.0",
173    //     note = "please use the `Value::constraint()` and `Value::base()` methods instead"
174    // )]
175    // #[allow(deprecated)]
176    pub fn new_with<F, C>(x: F, base: Base, prefix_constraint: C) -> Self
177    where
178        F: IntoF64,
179        C: AsRef<Constraint>,
180    {
181        let x: f64 = x.into_f64();
182
183        // Closest integral exponent (multiple of 3)
184        let exponent: i32 = base.integral_exponent_for(x);
185        // Clamp the exponent using the constraint on prefix
186        let prefix = Self::closest_prefix_for(exponent, prefix_constraint);
187
188        let mantissa = x / base.pow(prefix.exponent());
189
190        Value {
191            mantissa,
192            base,
193            prefix,
194        }
195    }
196
197    /// Converts `self` to a `f64`.
198    ///
199    /// # Example
200    ///
201    /// ```
202    /// use si_scale::prelude::{Base, Prefix, Value};
203    ///
204    /// let value = Value {
205    ///     mantissa: 1.3f64,
206    ///     prefix: Prefix::Unit,
207    ///     base: Base::B1000,
208    /// };
209    /// assert_eq!(value.to_f64(), 1.3);
210    /// ```
211    ///
212    pub fn to_f64(&self) -> f64 {
213        let scale = self.base.pow(self.prefix.exponent());
214        self.mantissa * scale
215    }
216
217    /// Returns a number that represents the sign of self.
218    ///
219    /// - `1.0` if the number is positive, `+0.0` or `INFINITY`
220    /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
221    /// - `NAN` if the number is `NAN`
222    ///
223    /// # Example
224    ///
225    /// ```
226    /// use std::convert::From;
227    /// use si_scale::value::Value;
228    ///
229    /// let number = -1.5e3f32;
230    /// let value: Value = number.into();
231    ///
232    /// assert_eq!(value.signum(), number.signum() as f64);
233    /// ```
234    ///
235    pub fn signum(&self) -> f64 {
236        self.mantissa.signum()
237    }
238
239    /// Returns the closest prefix for the provided exponent, respecting the
240    /// optional constraint.
241    ///
242    /// # Panics
243    ///
244    /// - If the `Custom` allowed prefixes is an empty vector.
245    ///
246    fn closest_prefix_for<C: AsRef<Constraint>>(exponent: i32, constraint: C) -> Prefix {
247        use std::convert::TryFrom;
248
249        match constraint.as_ref() {
250            Constraint::None => {
251                Prefix::try_from(exponent.clamp(Prefix::Yocto as i32, Prefix::Yotta as i32))
252                    .unwrap_or(Prefix::Unit)
253            }
254            Constraint::UnitOnly => Prefix::Unit,
255            Constraint::UnitAndAbove => {
256                Prefix::try_from(exponent.clamp(Prefix::Unit as i32, Prefix::Yotta as i32))
257                    .unwrap_or(Prefix::Unit)
258            }
259            Constraint::UnitAndBelow => {
260                Prefix::try_from(exponent.clamp(Prefix::Yocto as i32, Prefix::Unit as i32))
261                    .unwrap_or(Prefix::Unit)
262            }
263            Constraint::Custom(allowed_prefixes) => {
264                if allowed_prefixes.is_empty() {
265                    panic!("At least one prefix should be allowed");
266                }
267                let smallest_prefix = *allowed_prefixes.first().unwrap();
268                if exponent < smallest_prefix as i32 {
269                    return smallest_prefix;
270                }
271                allowed_prefixes
272                    .iter()
273                    .take_while(|&&prefix| prefix as i32 <= exponent)
274                    .cloned()
275                    .last()
276                    .unwrap_or(Prefix::Unit)
277            }
278        }
279    }
280}
281
282//
283// From Value -> f64
284//
285
286impl From<Value> for f64 {
287    fn from(value: Value) -> Self {
288        value.to_f64()
289    }
290}
291
292//
293// From number -> Value
294//
295
296macro_rules! impl_from_num_for_value {
297    ($t:ty) => {
298        impl From<$t> for Value {
299            fn from(x: $t) -> Self {
300                Value::new(x)
301            }
302        }
303
304        impl From<&$t> for Value {
305            fn from(x: &$t) -> Self {
306                Value::new(*x)
307            }
308        }
309    };
310}
311
312//
313// Display (simplistic)
314//
315
316impl fmt::Display for Value {
317    /// A basic but limited way to display the value; it does not allow
318    /// mantissa formatting. Consider using the
319    /// [`format_value!()`][`crate::format_value`] macro instead.
320    ///
321    /// # Example
322    ///
323    /// ```
324    /// use std::convert::From;
325    /// use si_scale::prelude::Value;
326    ///
327    /// let value: Value = 5.3e5.into();
328    ///
329    /// let actual = format!("{}", value);
330    /// let expected = "530 k".to_string();
331    /// assert_eq!(actual, expected);
332    /// ```
333    ///
334    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335        match self.prefix {
336            Prefix::Unit => write!(f, "{}", self.mantissa),
337            _ => write!(f, "{} {}", self.mantissa, self.prefix),
338        }
339    }
340}
341
342impl_from_num_for_value!(u8);
343impl_from_num_for_value!(i8);
344impl_from_num_for_value!(u16);
345impl_from_num_for_value!(i16);
346impl_from_num_for_value!(u32);
347impl_from_num_for_value!(i32);
348impl_from_num_for_value!(f32);
349impl_from_num_for_value!(f64);
350
351#[cfg(feature = "lossy-conversions")]
352impl_from_num_for_value!(u64);
353#[cfg(feature = "lossy-conversions")]
354impl_from_num_for_value!(i64);
355#[cfg(feature = "lossy-conversions")]
356impl_from_num_for_value!(usize);
357#[cfg(feature = "lossy-conversions")]
358impl_from_num_for_value!(isize);
359
360#[cfg(test)]
361mod tests {
362    use super::*;
363
364    #[test]
365    fn out_of_scale_values() {
366        let actual = Value::new(1e-28);
367        let expected = Value {
368            mantissa: 1e-4f64,
369            prefix: Prefix::Yocto,
370            base: Base::B1000,
371        };
372        assert_eq!(actual, expected);
373
374        let actual = Value::new(-1.5e28);
375        let expected = Value {
376            mantissa: -1.5e4f64,
377            prefix: Prefix::Yotta,
378            base: Base::B1000,
379        };
380        assert_eq!(actual, expected);
381    }
382
383    #[test]
384    fn unit_values() {
385        let actual = Value::new(1);
386        let expected = Value {
387            mantissa: 1f64,
388            prefix: Prefix::Unit,
389            base: Base::B1000,
390        };
391        assert_eq!(actual, expected);
392
393        let actual = Value::new(-1.3);
394        let expected = Value {
395            mantissa: -1.3f64,
396            prefix: Prefix::Unit,
397            base: Base::B1000,
398        };
399        assert_eq!(actual, expected);
400    }
401
402    #[test]
403    fn small_values() {
404        let actual = Value::new(0.1);
405        let expected = Value {
406            mantissa: 100f64,
407            prefix: Prefix::Milli,
408            base: Base::B1000,
409        };
410        assert_eq!(actual, expected);
411
412        let actual = Value::new(-0.1);
413        let expected = Value {
414            mantissa: -100f64,
415            prefix: Prefix::Milli,
416            base: Base::B1000,
417        };
418        assert_eq!(actual, expected);
419
420        let actual = Value::new(0.001);
421        let expected = Value {
422            mantissa: 1f64,
423            prefix: Prefix::Milli,
424            base: Base::B1000,
425        };
426        assert_eq!(actual, expected);
427
428        let actual = Value::new(-0.001);
429        let expected = Value {
430            mantissa: -1f64,
431            prefix: Prefix::Milli,
432            base: Base::B1000,
433        };
434        assert_eq!(actual, expected);
435
436        let actual = Value::new(0.000_1);
437        let expected = Value {
438            mantissa: 100.00000000000001f64,
439            prefix: Prefix::Micro,
440            base: Base::B1000,
441        };
442        assert_eq!(actual, expected);
443
444        let actual = Value::new(-0.000_1);
445        let expected = Value {
446            mantissa: -100.00000000000001f64,
447            prefix: Prefix::Micro,
448            base: Base::B1000,
449        };
450        assert_eq!(actual, expected);
451
452        let actual = Value::new(-1e-4);
453        let expected = Value {
454            mantissa: -100.00000000000001f64,
455            prefix: Prefix::Micro,
456            base: Base::B1000,
457        };
458        assert_eq!(actual, expected);
459
460        let actual = Value::new(-1e-8);
461        let expected = Value {
462            mantissa: -10f64,
463            prefix: Prefix::Nano,
464            base: Base::B1000,
465        };
466        assert_eq!(actual, expected);
467
468        let actual = Value::new(-1e-23);
469        let expected = Value {
470            mantissa: -10f64,
471            prefix: Prefix::Yocto,
472            base: Base::B1000,
473        };
474        assert_eq!(actual, expected);
475
476        let actual = Value::new(0.12345);
477        let expected = Value {
478            mantissa: 123.45f64,
479            prefix: Prefix::Milli,
480            base: Base::B1000,
481        };
482        assert_eq!(actual, expected);
483
484        let actual = Value::new(-0.12345);
485        let expected = Value {
486            mantissa: -123.45f64,
487            prefix: Prefix::Milli,
488            base: Base::B1000,
489        };
490        assert_eq!(actual, expected);
491
492        let actual = Value::new(0.01234);
493        let expected = Value {
494            mantissa: 12.34f64,
495            prefix: Prefix::Milli,
496            base: Base::B1000,
497        };
498        assert_eq!(actual, expected);
499
500        let actual = Value::new(-0.01234);
501        let expected = Value {
502            mantissa: -12.34f64,
503            prefix: Prefix::Milli,
504            base: Base::B1000,
505        };
506        assert_eq!(actual, expected);
507
508        let actual = Value::new(0.001234);
509        let expected = Value {
510            mantissa: 1.234f64,
511            prefix: Prefix::Milli,
512            base: Base::B1000,
513        };
514        assert_eq!(actual, expected);
515
516        let actual = Value::new(-0.001234);
517        let expected = Value {
518            mantissa: -1.234f64,
519            prefix: Prefix::Milli,
520            base: Base::B1000,
521        };
522        assert_eq!(actual, expected);
523
524        let actual = Value::new(0.000_123_400);
525        let expected = Value {
526            mantissa: 123.39999999999999f64,
527            prefix: Prefix::Micro,
528            base: Base::B1000,
529        };
530        assert_eq!(actual, expected);
531
532        let actual = Value::new(-0.000_123_400);
533        let expected = Value {
534            mantissa: -123.39999999999999f64,
535            prefix: Prefix::Micro,
536            base: Base::B1000,
537        };
538        assert_eq!(actual, expected);
539    }
540
541    #[test]
542    fn large_values() {
543        let actual = Value::new(1234);
544        let expected = Value {
545            mantissa: 1.234f64,
546            prefix: Prefix::Kilo,
547            base: Base::B1000,
548        };
549        assert_eq!(actual, expected);
550
551        let actual = Value::new(123_456);
552        let expected = Value {
553            mantissa: 123.456f64,
554            prefix: Prefix::Kilo,
555            base: Base::B1000,
556        };
557        assert_eq!(actual, expected);
558
559        let actual = Value::new(123_456_000);
560        let expected = Value {
561            mantissa: 123.456f64,
562            prefix: Prefix::Mega,
563            base: Base::B1000,
564        };
565        assert_eq!(actual, expected);
566
567        let actual = Value::new(-123_456_000);
568        let expected = Value {
569            mantissa: -123.456f64,
570            prefix: Prefix::Mega,
571            base: Base::B1000,
572        };
573        assert_eq!(actual, expected);
574    }
575
576    #[test]
577    fn from_numbers() {
578        let actual = Value::from(0.1f32);
579        let expected = Value {
580            mantissa: 100.00000149011612f64,
581            prefix: Prefix::Milli,
582            base: Base::B1000,
583        };
584        assert_eq!(actual, expected);
585
586        let actual = Value::from(-0.1);
587        let expected = Value {
588            mantissa: -100f64,
589            prefix: Prefix::Milli,
590            base: Base::B1000,
591        };
592        assert_eq!(actual, expected);
593
594        let actual = Value::from(1.5);
595        let expected = Value {
596            mantissa: 1.5f64,
597            prefix: Prefix::Unit,
598            base: Base::B1000,
599        };
600        assert_eq!(actual, expected);
601
602        let actual = Value::from(-1.5);
603        let expected = Value {
604            mantissa: -1.5f64,
605            prefix: Prefix::Unit,
606            base: Base::B1000,
607        };
608        assert_eq!(actual, expected);
609
610        let actual = Value::from(15u32);
611        let expected = Value {
612            mantissa: 15f64,
613            prefix: Prefix::Unit,
614            base: Base::B1000,
615        };
616        assert_eq!(actual, expected);
617
618        let actual = Value::from(-1.5e28);
619        let expected = Value {
620            mantissa: -1.5e4f64,
621            prefix: Prefix::Yotta,
622            base: Base::B1000,
623        };
624        assert_eq!(actual, expected);
625    }
626
627    #[test]
628    fn from_ref_number() {
629        let number = 0.1;
630        let actual = Value::from(&number);
631        let expected = Value {
632            mantissa: 100f64,
633            prefix: Prefix::Milli,
634            base: Base::B1000,
635        };
636        assert_eq!(actual, expected);
637
638        let number = 10_000_000u32;
639        let actual = Value::from(&number);
640        let expected = Value {
641            mantissa: 10f64,
642            prefix: Prefix::Mega,
643            base: Base::B1000,
644        };
645        assert_eq!(actual, expected);
646    }
647
648    #[test]
649    fn into_f64() {
650        let x = 0.1;
651        let actual: f64 = Value::from(x).into();
652        let expected = x;
653        assert_eq!(actual, expected);
654
655        let x = -0.1;
656        let actual: f64 = Value::from(x).into();
657        let expected = x;
658        assert_eq!(actual, expected);
659
660        let x = 1.500;
661        let actual: f64 = Value::from(x).into();
662        let expected = x;
663        assert_eq!(actual, expected);
664
665        let x = -1.500;
666        let actual: f64 = Value::from(x).into();
667        let expected = x;
668        assert_eq!(actual, expected);
669    }
670
671    #[test]
672    fn large_value_with_base_1024() {
673        let actual = Value::new_with(1, Base::B1024, Constraint::None);
674        let expected = Value {
675            mantissa: 1f64,
676            prefix: Prefix::Unit,
677            base: Base::B1024,
678        };
679        assert_eq!(actual, expected);
680
681        let actual = Value::new_with(16, Base::B1024, Constraint::None);
682        let expected = Value {
683            mantissa: 16f64,
684            prefix: Prefix::Unit,
685            base: Base::B1024,
686        };
687        assert_eq!(actual, expected);
688
689        let actual = Value::new_with(1024, Base::B1024, Constraint::None);
690        let expected = Value {
691            mantissa: 1f64,
692            prefix: Prefix::Kilo,
693            base: Base::B1024,
694        };
695        assert_eq!(actual, expected);
696
697        let actual = Value::new_with(1.6 * 1024f32, Base::B1024, Constraint::None);
698        let expected = Value {
699            mantissa: 1.600000023841858f64,
700            prefix: Prefix::Kilo,
701            base: Base::B1024,
702        };
703        assert_eq!(actual, expected);
704
705        let actual = Value::new_with(16 * 1024 * 1024, Base::B1024, Constraint::None);
706        let expected = Value {
707            mantissa: 16f64,
708            prefix: Prefix::Mega,
709            base: Base::B1024,
710        };
711        assert_eq!(actual, expected);
712    }
713
714    #[test]
715    fn values_with_prefix_constraints() {
716        // For instance, seconds are never expressed as kilo-seconds, so
717        // we must use constraints.
718        let actual = Value::new_with(1325, Base::B1000, Constraint::UnitAndBelow);
719        let expected = Value {
720            mantissa: 1325f64,
721            prefix: Prefix::Unit,
722            base: Base::B1000,
723        };
724        assert_eq!(actual, expected);
725
726        // In the same spirit, there can be no milli-bytes.
727        let actual = Value::new_with(0.015, Base::B1024, Constraint::UnitAndAbove);
728        let expected = Value {
729            mantissa: 0.015,
730            prefix: Prefix::Unit,
731            base: Base::B1024,
732        };
733        assert_eq!(actual, expected);
734
735        // The `UnitOnly` constraint prevents any scaling
736        let actual = Value::new_with(0.015, Base::B1000, Constraint::UnitOnly);
737        let expected = Value {
738            mantissa: 0.015,
739            prefix: Prefix::Unit,
740            base: Base::B1000,
741        };
742        assert_eq!(actual, expected);
743    }
744
745    /// If no prefix constraint is set, then the function returns the best
746    /// prefix if the exponent is a multiple of 3 and between `-24` and `24`
747    /// (incl.).
748    #[test]
749    fn closest_prefix_without_constraint() {
750        let exponent = -24;
751        let actual = Value::closest_prefix_for(exponent, Constraint::None);
752        let expected = Prefix::Yocto;
753        assert_eq!(actual, expected);
754
755        let exponent = 0;
756        let actual = Value::closest_prefix_for(exponent, Constraint::None);
757        let expected = Prefix::Unit;
758        assert_eq!(actual, expected);
759
760        let exponent = 24;
761        let actual = Value::closest_prefix_for(exponent, Constraint::None);
762        let expected = Prefix::Yotta;
763        assert_eq!(actual, expected);
764
765        let exponent = 30;
766        let actual = Value::closest_prefix_for(exponent, Constraint::None);
767        let expected = Prefix::Yotta;
768        assert_eq!(actual, expected);
769
770        let exponent = 1; // should never happen
771        let actual = Value::closest_prefix_for(exponent, Constraint::None);
772        let expected = Prefix::Unit;
773        assert_eq!(actual, expected);
774    }
775
776    /// If the allowed prefixes are `Constraint::UnitAndAbove`, the function
777    /// returns the corresponding prefix if the exponent is greater or equal
778    /// than `0`.
779    #[test]
780    fn closest_prefix_with_unitandabove() {
781        let constraint = Constraint::UnitAndAbove;
782
783        let exponent = -24;
784        let actual = Value::closest_prefix_for(exponent, &constraint);
785        let expected = Prefix::Unit;
786        assert_eq!(actual, expected);
787
788        let exponent = 0;
789        let actual = Value::closest_prefix_for(exponent, &constraint);
790        let expected = Prefix::Unit;
791        assert_eq!(actual, expected);
792
793        let exponent = 24;
794        let actual = Value::closest_prefix_for(exponent, &constraint);
795        let expected = Prefix::Yotta;
796        assert_eq!(actual, expected);
797
798        let exponent = 30;
799        let actual = Value::closest_prefix_for(exponent, &constraint);
800        let expected = Prefix::Yotta;
801        assert_eq!(actual, expected);
802
803        let exponent = 1; // should never happen
804        let actual = Value::closest_prefix_for(exponent, &constraint);
805        let expected = Prefix::Unit;
806        assert_eq!(actual, expected);
807    }
808
809    /// If the allowed prefixes are `Constraint::UnitAndBelow`, the function
810    /// returns the corresponding prefix if the exponent is smaller or equal
811    /// than `0`.
812    #[test]
813    fn closest_prefix_with_unitandbelow() {
814        let constraint = Constraint::UnitAndBelow;
815
816        let exponent = -24;
817        let actual = Value::closest_prefix_for(exponent, &constraint);
818        let expected = Prefix::Yocto;
819        assert_eq!(actual, expected);
820
821        let exponent = 0;
822        let actual = Value::closest_prefix_for(exponent, &constraint);
823        let expected = Prefix::Unit;
824        assert_eq!(actual, expected);
825
826        let exponent = 24;
827        let actual = Value::closest_prefix_for(exponent, &constraint);
828        let expected = Prefix::Unit;
829        assert_eq!(actual, expected);
830
831        let exponent = -30;
832        let actual = Value::closest_prefix_for(exponent, &constraint);
833        let expected = Prefix::Yocto;
834        assert_eq!(actual, expected);
835
836        let exponent = -1; // should never happen
837        let actual = Value::closest_prefix_for(exponent, &constraint);
838        let expected = Prefix::Unit;
839        assert_eq!(actual, expected);
840    }
841
842    /// If the allowed prefixes are `Constraint::Custom(...)`, the function
843    /// returns the corresponding prefix if the exponent matches one of them.
844    #[test]
845    fn closest_prefix_with_custom() {
846        let constraint = Constraint::Custom(vec![Prefix::Milli, Prefix::Unit, Prefix::Kilo]);
847
848        let exponent = -24;
849        let actual = Value::closest_prefix_for(exponent, &constraint);
850        let expected = Prefix::Milli;
851        assert_eq!(actual, expected);
852
853        let exponent = -3;
854        let actual = Value::closest_prefix_for(exponent, &constraint);
855        let expected = Prefix::Milli;
856        assert_eq!(actual, expected);
857
858        let exponent = 0;
859        let actual = Value::closest_prefix_for(exponent, &constraint);
860        let expected = Prefix::Unit;
861        assert_eq!(actual, expected);
862
863        let exponent = 3;
864        let actual = Value::closest_prefix_for(exponent, &constraint);
865        let expected = Prefix::Kilo;
866        assert_eq!(actual, expected);
867
868        let exponent = 24;
869        let actual = Value::closest_prefix_for(exponent, &constraint);
870        let expected = Prefix::Kilo;
871        assert_eq!(actual, expected);
872
873        let exponent = -30;
874        let actual = Value::closest_prefix_for(exponent, &constraint);
875        let expected = Prefix::Milli;
876        assert_eq!(actual, expected);
877
878        let exponent = -1; // should never happen
879        let actual = Value::closest_prefix_for(exponent, &constraint);
880        let expected = Prefix::Milli;
881        assert_eq!(actual, expected);
882    }
883
884    #[test]
885    #[should_panic]
886    fn closest_prefix_with_custom_empty() {
887        let constraint = Constraint::Custom(vec![]);
888
889        let exponent = 3;
890        Value::closest_prefix_for(exponent, constraint);
891    }
892}