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}