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