diff --git a/src/number.rs b/src/number.rs index 84b5a5f..79c82e7 100644 --- a/src/number.rs +++ b/src/number.rs @@ -100,6 +100,36 @@ pub trait PrimitiveNumber: + for<'a> core::ops::Sub<&'a Self, Output = Self> + for<'a> core::ops::SubAssign<&'a Self> { + /// Constant values `0..=127` in this type. + /// + /// Since [literal expressions] can't be used for generic types, this constant array provides + /// an alternative way to access particular values. It contains the complete set of numbers + /// that all primitive numeric types have in common, where `CONST[i]` equals the index `i` + /// converted to `Self`. + /// + /// For floating-point types with signed zeros, `-0.0` and `+0.0`, `CONST[0]` is the positive + /// value. + /// + /// [literal expressions]: https://doc.rust-lang.org/reference/expressions/literal-expr.html + /// + /// # Examples + /// + /// [Milü](https://en.wikipedia.org/wiki/Mil%C3%BC) is an approximation of π as the ratio + /// `355 / 113`, which can also be written as `3 + 16 / 113`. + /// + /// ``` + /// use num_primitive::{PrimitiveFloat, PrimitiveNumber}; + /// + /// fn milu() -> T { + /// T::CONST[3] + T::CONST[16] / T::CONST[113] + /// } + /// + /// assert!((milu::() - f64::PI).abs() < 1e-6); + /// assert!((milu::() - f32::PI).abs() < 1e-6); + /// assert_eq!(milu::(), 3); + /// ``` + const CONST: [Self; 128]; + /// An array of bytes used by methods like [`from_be_bytes`][Self::from_be_bytes] and /// [`to_be_bytes`][Self::to_be_bytes]. It is effectively `[u8; size_of::()]`. type Bytes: PrimitiveBytes; @@ -251,6 +281,17 @@ macro_rules! impl_primitive { impl Sealed for &$Number {} impl PrimitiveNumber for $Number { + const CONST: [Self; 128] = const { + let mut constants = [0 as Self; 128]; + let mut i = 1; + while i < 128 { + constants[i] = i as Self; + assert!(constants[i] as usize == i); + i += 1; + } + constants + }; + type Bytes = [u8; size_of::()]; forward! { diff --git a/src/tests.rs b/src/tests.rs index 99aba34..5f9cfa9 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -111,3 +111,37 @@ fn try_into() { } check(0i32, 0u32); } + +#[test] +fn constants() { + fn check() { + for (i, c) in T::CONST.into_iter().enumerate() { + assert_eq!(T::as_from(i), c); + assert_eq!(i, c.as_to()); + } + // explicitly const context + assert_eq!(T::as_from(0i32), const { T::CONST[0] }); + assert_eq!(T::as_from(1i32), const { T::CONST[1] }); + assert_eq!(T::as_from(42i32), const { T::CONST[42] }); + } + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); + check::(); +} + +#[test] +fn constant_float_zero() { + assert!(::CONST[0].is_sign_positive()); + assert!(::CONST[0].is_sign_positive()); +}