Skip to main content

const_num_traits/
sign.rs

1use core::num::Wrapping;
2use core::ops::Neg;
3
4use crate::Num;
5use crate::float::FloatCore;
6
7c0nst::c0nst! {
8/// Returns the sign of a number.
9///
10/// This is the standalone atom for the `signum` capability; [`Signed`]
11/// inherits it as a supertrait (the same extraction pattern as
12/// `PrimBits`/`PrimInt`).
13pub c0nst trait Signum: Sized {
14    /// The (owned) sign result type.
15    type Output;
16
17    /// Returns the sign of the number.
18    ///
19    /// For `f32` and `f64`:
20    ///
21    /// * `1.0` if the number is positive, `+0.0` or `INFINITY`
22    /// * `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
23    /// * `NaN` if the number is `NaN`
24    ///
25    /// For signed integers:
26    ///
27    /// * `0` if the number is zero
28    /// * `1` if the number is positive
29    /// * `-1` if the number is negative
30    fn signum(self) -> Self::Output;
31}
32}
33
34c0nst::c0nst! {
35/// Useful functions for signed numbers (i.e. numbers that can be negative).
36pub c0nst trait Signed: Sized + [c0nst] Num + [c0nst] Neg + [c0nst] Signum {
37    /// Computes the absolute value.
38    ///
39    /// For `f32` and `f64`, `NaN` will be returned if the number is `NaN`.
40    ///
41    /// For signed integers the result is always non-negative and **total**:
42    /// `-::MIN` is unrepresentable, so `abs(::MIN)` saturates to `::MAX` (the
43    /// nearest representable magnitude) rather than overflowing. This is
44    /// deliberately consistent across const evaluation, debug and release —
45    /// unlike the inherent `i32::abs`, which panics/overflows on `::MIN`. For a
46    /// different overflow policy use [`WrappingAbs`](crate::WrappingAbs),
47    /// [`CheckedAbs`](crate::CheckedAbs) or [`StrictAbs`](crate::StrictAbs); for
48    /// a statically-proven total `abs`, use [`NonMin`](crate::NonMin).
49    fn abs(self) -> <Self as Neg>::Output;
50
51    /// The positive difference of two numbers.
52    ///
53    /// Returns `zero` if the number is less than or equal to `other`, otherwise the difference
54    /// between `self` and `other` is returned.
55    fn abs_sub(self, other: Self) -> <Self as Neg>::Output;
56
57    /// Returns true if the number is positive and false if the number is zero or negative.
58    fn is_positive(self) -> bool;
59
60    /// Returns true if the number is negative and false if the number is zero or positive.
61    fn is_negative(self) -> bool;
62}
63}
64
65macro_rules! signed_impl {
66    ($($t:ty)*) => ($(
67        c0nst::c0nst! {
68        c0nst impl Signum for $t {
69            type Output = $t;
70            #[inline]
71            fn signum(self) -> $t {
72                match self {
73                    n if n > 0 => 1,
74                    0 => 0,
75                    _ => -1,
76                }
77            }
78        }
79        }
80
81        c0nst::c0nst! {
82        c0nst impl Signed for $t {
83            #[inline]
84            fn abs(self) -> $t {
85                // Total and identical in const / debug / release: an absolute
86                // value is non-negative, so `MIN` saturates to `MAX` (the
87                // nearest representable magnitude) rather than overflowing.
88                // Use `WrappingAbs`/`CheckedAbs`/`StrictAbs` for other policies.
89                self.saturating_abs()
90            }
91
92            #[inline]
93            fn abs_sub(self, other: $t) -> $t {
94                // Saturating subtraction keeps the positive difference total
95                // and const/runtime-consistent when `self - other` would
96                // overflow (e.g. `MAX - MIN`).
97                if self <= other { 0 } else { self.saturating_sub(other) }
98            }
99
100            #[inline]
101            fn is_positive(self) -> bool { self > 0 }
102
103            #[inline]
104            fn is_negative(self) -> bool { self < 0 }
105        }
106        }
107    )*)
108}
109
110signed_impl!(isize i8 i16 i32 i64 i128);
111
112// `Wrapping<T>: Num` is itself a non-const impl (std's `PartialEq` on
113// `Wrapping<T>` isn't const), so these stay non-const.
114impl<T: Signum<Output = T>> Signum for Wrapping<T> {
115    type Output = Wrapping<T>;
116    #[inline]
117    fn signum(self) -> Self {
118        Wrapping(self.0.signum())
119    }
120}
121
122impl<T: Signed + Neg<Output = T> + Signum<Output = T>> Signed for Wrapping<T>
123where
124    Wrapping<T>: Num + Neg<Output = Wrapping<T>>,
125{
126    #[inline]
127    fn abs(self) -> Self {
128        Wrapping(self.0.abs())
129    }
130
131    #[inline]
132    fn abs_sub(self, other: Self) -> Self {
133        Wrapping(self.0.abs_sub(other.0))
134    }
135
136    #[inline]
137    fn is_positive(self) -> bool {
138        self.0.is_positive()
139    }
140
141    #[inline]
142    fn is_negative(self) -> bool {
143        self.0.is_negative()
144    }
145}
146
147// Float Signed/Signum stay non-const: `FloatCore::signum` isn't const-callable.
148macro_rules! signed_float_impl {
149    ($t:ty) => {
150        impl Signum for $t {
151            type Output = $t;
152            /// # Returns
153            ///
154            /// - `1.0` if the number is positive, `+0.0` or `INFINITY`
155            /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
156            /// - `NAN` if the number is NaN
157            #[inline]
158            fn signum(self) -> $t {
159                FloatCore::signum(self)
160            }
161        }
162
163        impl Signed for $t {
164            /// Computes the absolute value. Returns `NAN` if the number is `NAN`.
165            #[inline]
166            fn abs(self) -> $t {
167                FloatCore::abs(self)
168            }
169
170            /// The positive difference of two numbers. Returns `0.0` if the number is
171            /// less than or equal to `other`, otherwise the difference between`self`
172            /// and `other` is returned.
173            #[inline]
174            fn abs_sub(self, other: $t) -> $t {
175                if self <= other { 0. } else { self - other }
176            }
177
178            /// Returns `true` if the number is positive, including `+0.0` and `INFINITY`
179            #[inline]
180            fn is_positive(self) -> bool {
181                FloatCore::is_sign_positive(self)
182            }
183
184            /// Returns `true` if the number is negative, including `-0.0` and `NEG_INFINITY`
185            #[inline]
186            fn is_negative(self) -> bool {
187                FloatCore::is_sign_negative(self)
188            }
189        }
190    };
191}
192
193signed_float_impl!(f32);
194signed_float_impl!(f64);
195
196c0nst::c0nst! {
197/// Computes the absolute value.
198///
199/// For `f32` and `f64`, `NaN` will be returned if the number is `NaN`
200///
201/// For signed integers, `::MIN` will be returned if the number is `::MIN`.
202#[inline(always)]
203pub c0nst fn abs<T: [c0nst] Signed + [c0nst] Neg<Output = T> + [c0nst] Destruct>(value: T) -> T {
204    value.abs()
205}
206}
207
208c0nst::c0nst! {
209/// The positive difference of two numbers.
210///
211/// Returns zero if `x` is less than or equal to `y`, otherwise the difference
212/// between `x` and `y` is returned.
213#[inline(always)]
214pub c0nst fn abs_sub<T: [c0nst] Signed + [c0nst] Neg<Output = T> + [c0nst] Destruct>(x: T, y: T) -> T {
215    x.abs_sub(y)
216}
217}
218
219c0nst::c0nst! {
220/// Returns the sign of the number.
221///
222/// For `f32` and `f64`:
223///
224/// * `1.0` if the number is positive, `+0.0` or `INFINITY`
225/// * `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
226/// * `NaN` if the number is `NaN`
227///
228/// For signed integers:
229///
230/// * `0` if the number is zero
231/// * `1` if the number is positive
232/// * `-1` if the number is negative
233#[inline(always)]
234pub c0nst fn signum<T: [c0nst] Signed + [c0nst] Signum<Output = T> + [c0nst] Destruct>(value: T) -> T {
235    value.signum()
236}
237}
238
239c0nst::c0nst! {
240/// A trait for values which cannot be negative
241pub c0nst trait Unsigned: [c0nst] Num {}
242}
243
244macro_rules! empty_trait_impl {
245    ($name:ident for $($t:ty)*) => ($(
246        c0nst::c0nst! {
247        c0nst impl $name for $t {}
248        }
249    )*)
250}
251
252empty_trait_impl!(Unsigned for usize u8 u16 u32 u64 u128);
253
254// Same `Wrapping<T>: Num` story as `Signed`: non-const blanket impl.
255impl<T: Unsigned> Unsigned for Wrapping<T> where Wrapping<T>: Num {}
256
257#[test]
258fn unsigned_wrapping_is_unsigned() {
259    fn require_unsigned<T: Unsigned>(_: &T) {}
260    require_unsigned(&Wrapping(42_u32));
261}
262
263#[test]
264fn signed_wrapping_is_signed() {
265    fn require_signed<T: Signed>(_: &T) {}
266    require_signed(&Wrapping(-42));
267}
268
269#[test]
270fn abs_is_total_and_non_negative() {
271    // ordinary cases
272    assert_eq!(Signed::abs(-7i32), 7);
273    assert_eq!(Signed::abs(7i32), 7);
274    assert_eq!(Signed::abs(0i32), 0);
275    // the MIN case: saturates to MAX (non-negative, total — never panics)
276    assert_eq!(Signed::abs(i8::MIN), i8::MAX);
277    assert_eq!(Signed::abs(i32::MIN), i32::MAX);
278    assert_eq!(Signed::abs(i64::MIN), i64::MAX);
279    // abs_sub stays total when the difference would overflow
280    assert_eq!(Signed::abs_sub(i32::MAX, i32::MIN), i32::MAX);
281    assert_eq!(Signed::abs_sub(5i32, 8), 0);
282    assert_eq!(Signed::abs_sub(8i32, 5), 3);
283}