fixed/
prim_traits.rs

1// Copyright © 2018–2025 Trevor Spiteri
2
3// This library is free software: you can redistribute it and/or modify it under
4// the terms of either
5//
6//   * the Apache License, Version 2.0 or
7//   * the MIT License
8//
9// at your option.
10//
11// You should have recieved copies of the Apache License and the MIT License
12// along with the library. If not, see
13// <https://www.apache.org/licenses/LICENSE-2.0> and
14// <https://opensource.org/licenses/MIT>.
15
16#![allow(deprecated)]
17
18use crate::float_helper;
19use crate::helpers::{FloatKind, FromFloatHelper, Private};
20use crate::int_helper::IntFixed;
21use crate::traits::{Fixed, FixedEquiv, FromFixed, ToFixed};
22use crate::types::extra::U0;
23use crate::{
24    F128, F128Bits, FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32,
25    FixedU64, FixedU128,
26};
27use bytemuck::TransparentWrapper;
28use half::{bf16 as half_bf16, f16 as half_f16};
29
30impl ToFixed for bool {
31    /// Converts a [`bool`] to a fixed-point number.
32    ///
33    /// # Panics
34    ///
35    /// When debug assertions are enabled, panics if the value does
36    /// not fit. When debug assertions are not enabled, the wrapped
37    /// value can be returned, but it is not considered a breaking
38    /// change if in the future it panics; if wrapping is required use
39    /// [`wrapping_to_fixed`] instead.
40    ///
41    /// [`wrapping_to_fixed`]: ToFixed::wrapping_to_fixed
42    #[inline]
43    #[track_caller]
44    fn to_fixed<F: Fixed>(self) -> F {
45        ToFixed::to_fixed(u8::from(self))
46    }
47
48    /// Converts a [`bool`] to a fixed-point number if it fits, otherwise returns [`None`].
49    #[inline]
50    fn checked_to_fixed<F: Fixed>(self) -> Option<F> {
51        ToFixed::checked_to_fixed(u8::from(self))
52    }
53
54    /// Convert a [`bool`] to a fixed-point number, saturating if it does not fit.
55    #[inline]
56    fn saturating_to_fixed<F: Fixed>(self) -> F {
57        ToFixed::saturating_to_fixed(u8::from(self))
58    }
59
60    /// Converts a [`bool`] to a fixed-point number, wrapping if it does not fit.
61    #[inline]
62    fn wrapping_to_fixed<F: Fixed>(self) -> F {
63        ToFixed::wrapping_to_fixed(u8::from(self))
64    }
65
66    /// Converts a [`bool`] to a fixed-point number.
67    ///
68    /// Returns a [tuple] of the fixed-point number and a [`bool`]
69    /// indicating whether an overflow has occurred. On overflow, the
70    /// wrapped value is returned.
71    #[inline]
72    fn overflowing_to_fixed<F: Fixed>(self) -> (F, bool) {
73        ToFixed::overflowing_to_fixed(u8::from(self))
74    }
75
76    /// Converts a [`bool`] to a fixed-point number, panicking if it
77    /// does not fit.
78    ///
79    /// # Panics
80    ///
81    /// Panics if the value does not fit, even when debug assertions
82    /// are not enabled.
83    #[inline]
84    #[track_caller]
85    fn unwrapped_to_fixed<F: Fixed>(self) -> F {
86        ToFixed::unwrapped_to_fixed(u8::from(self))
87    }
88}
89
90macro_rules! impl_int {
91    ($Int:ident $(, $Equiv:ident)?) => {
92        impl FromFixed for $Int {
93            /// Converts a fixed-point number to an integer.
94            ///
95            /// Any fractional bits are discarded, which rounds towards &minus;∞.
96            ///
97            /// # Panics
98            ///
99            /// When debug assertions are enabled, panics if the value
100            /// does not fit. When debug assertions are not enabled,
101            /// the wrapped value can be returned, but it is not
102            /// considered a breaking change if in the future it
103            /// panics; if wrapping is required use
104            /// [`wrapping_from_fixed`] instead.
105            ///
106            /// [`wrapping_from_fixed`]: FromFixed::wrapping_from_fixed
107            #[inline]
108            #[track_caller]
109            fn from_fixed<F: Fixed>(src: F) -> Self {
110                IntFixed::<$Int>::int(FromFixed::from_fixed(src))
111            }
112
113            /// Converts a fixed-point number to an integer if it fits, otherwise returns [`None`].
114            ///
115            /// Any fractional bits are discarded, which rounds towards &minus;∞.
116            #[inline]
117            fn checked_from_fixed<F: Fixed>(src: F) -> Option<Self> {
118                FromFixed::checked_from_fixed(src).map(IntFixed::<$Int>::int)
119            }
120
121            /// Converts a fixed-point number to an integer, saturating if it does not fit.
122            ///
123            /// Any fractional bits are discarded, which rounds towards &minus;∞.
124            #[inline]
125            fn saturating_from_fixed<F: Fixed>(src: F) -> Self {
126                IntFixed::<$Int>::int(FromFixed::saturating_from_fixed(src))
127            }
128
129            /// Converts a fixed-point number to an integer, wrapping if it does not fit.
130            ///
131            /// Any fractional bits are discarded, which rounds towards &minus;∞.
132            #[inline]
133            fn wrapping_from_fixed<F: Fixed>(src: F) -> Self {
134                IntFixed::<$Int>::int(FromFixed::wrapping_from_fixed(src))
135            }
136
137            /// Converts a fixed-point number to an integer.
138            ///
139            /// Returns a [tuple] of the value and a [`bool`] indicating whether
140            /// an overflow has occurred. On overflow, the wrapped value is
141            /// returned.
142            ///
143            /// Any fractional bits are discarded, which rounds towards &minus;∞.
144            #[inline]
145            fn overflowing_from_fixed<F: Fixed>(src: F) -> (Self, bool) {
146                let (fixed, overflow) = FromFixed::overflowing_from_fixed(src);
147                (IntFixed::<$Int>::int(fixed), overflow)
148            }
149
150            /// Converts a fixed-point number to an integer, panicking if it does not fit.
151            ///
152            /// Any fractional bits are discarded, which rounds towards &minus;∞.
153            ///
154            /// # Panics
155            ///
156            /// Panics if the value
157            /// does not fit, even when debug assertions are not enabled.
158            #[inline]
159            #[track_caller]
160            fn unwrapped_from_fixed<F: Fixed>(src: F) -> Self {
161                IntFixed::<$Int>::int(FromFixed::unwrapped_from_fixed(src))
162            }
163        }
164
165        impl ToFixed for $Int {
166            /// Converts an integer to a fixed-point number.
167            ///
168            /// # Panics
169            ///
170            /// When debug assertions are enabled, panics if the value
171            /// does not fit. When debug assertions are not enabled,
172            /// the wrapped value can be returned, but it is not
173            /// considered a breaking change if in the future it
174            /// panics; if wrapping is required use
175            /// [`wrapping_to_fixed`] instead.
176            ///
177            /// [`wrapping_to_fixed`]: ToFixed::wrapping_to_fixed
178            #[inline]
179            #[track_caller]
180            fn to_fixed<F: Fixed>(self) -> F {
181                ToFixed::to_fixed(IntFixed(self).fixed())
182            }
183
184            /// Converts an integer to a fixed-point number if it fits, otherwise returns [`None`].
185            #[inline]
186            fn checked_to_fixed<F: Fixed>(self) -> Option<F> {
187                ToFixed::checked_to_fixed(IntFixed(self).fixed())
188            }
189
190            /// Converts an integer to a fixed-point number, saturating if it does not fit.
191            #[inline]
192            fn saturating_to_fixed<F: Fixed>(self) -> F {
193                ToFixed::saturating_to_fixed(IntFixed(self).fixed())
194            }
195
196            /// Converts an integer to a fixed-point number, wrapping if it does not fit.
197            #[inline]
198            fn wrapping_to_fixed<F: Fixed>(self) -> F {
199                ToFixed::wrapping_to_fixed(IntFixed(self).fixed())
200            }
201
202            /// Converts an integer to a fixed-point number.
203            ///
204            /// Returns a [tuple] of the fixed-point number and a [`bool`]
205            /// indicating whether an overflow has occurred. On overflow, the
206            /// wrapped value is returned.
207            #[inline]
208            fn overflowing_to_fixed<F: Fixed>(self) -> (F, bool) {
209                ToFixed::overflowing_to_fixed(IntFixed(self).fixed())
210            }
211
212            /// Converts an integer to a fixed-point number, panicking if it does not fit.
213            ///
214            /// # Panics
215            ///
216            /// Panics if the value does not fit, even when debug
217            /// assertions are not enabled.
218            #[inline]
219            #[track_caller]
220            fn unwrapped_to_fixed<F: Fixed>(self) -> F {
221                ToFixed::unwrapped_to_fixed(IntFixed(self).fixed())
222            }
223        }
224
225        $(
226            impl FixedEquiv for $Int {
227                type Equiv = $Equiv<U0>;
228
229                #[inline]
230                fn to_fixed_equiv(self) -> $Equiv<U0> {
231                    $Equiv::from_bits(self)
232                }
233
234                #[inline]
235                fn as_fixed_equiv(&self) -> &$Equiv<U0> {
236                    $Equiv::wrap_ref(self)
237                }
238
239                #[inline]
240                fn as_fixed_equiv_mut(&mut self) -> &mut $Equiv<U0> {
241                    $Equiv::wrap_mut(self)
242                }
243
244                #[inline]
245                fn from_fixed_equiv(f: $Equiv<U0>) -> $Int {
246                    f.to_bits()
247                }
248
249                #[inline]
250                fn ref_from_fixed_equiv(f: &$Equiv<U0>) -> &$Int {
251                    &f.bits
252                }
253
254                #[inline]
255                fn mut_from_fixed_equiv(f: &mut $Equiv<U0>) -> &mut $Int {
256                    &mut f.bits
257                }
258            }
259        )*
260    };
261}
262
263impl_int! { i8, FixedI8 }
264impl_int! { i16, FixedI16 }
265impl_int! { i32, FixedI32 }
266impl_int! { i64, FixedI64 }
267impl_int! { i128, FixedI128 }
268impl_int! { isize }
269impl_int! { u8, FixedU8 }
270impl_int! { u16, FixedU16 }
271impl_int! { u32, FixedU32 }
272impl_int! { u64, FixedU64 }
273impl_int! { u128, FixedU128 }
274impl_int! { usize }
275
276macro_rules! impl_float {
277    ($Float:ident, $link:expr, $overflows_fmt:expr, $overflows_filt:expr) => {
278        impl FromFixed for $Float {
279            /// Converts a fixed-point number to a floating-point number.
280            ///
281            /// Rounding is to the nearest, with ties rounded to even.
282            ///
283            /// # Panics
284            ///
285            /// When debug assertions are enabled, panics if the value
286            /// does not fit. When debug assertions are not enabled,
287            /// the wrapped value can be returned, but it is not
288            /// considered a breaking change if in the future it
289            /// panics; if wrapping is required use
290            /// [`wrapping_from_fixed`] instead.
291            ///
292            /// [`wrapping_from_fixed`]: FromFixed::wrapping_from_fixed
293            #[inline]
294            #[track_caller]
295            fn from_fixed<F: Fixed>(src: F) -> Self {
296                let helper = src.to_float_helper(Private);
297                float_helper::$Float::from_to_float_helper(helper, F::FRAC_NBITS, F::INT_NBITS)
298            }
299
300            /// Converts a fixed-point number to a floating-point
301            /// number if it fits, otherwise returns [`None`].
302            ///
303            /// Rounding is to the nearest, with ties rounded to even.
304            #[inline]
305            fn checked_from_fixed<F: Fixed>(src: F) -> Option<Self> {
306                Some(FromFixed::from_fixed(src))
307            }
308
309            /// Converts a fixed-point number to a floating-point
310            /// number, saturating if it does not fit.
311            ///
312            /// Rounding is to the nearest, with ties rounded to even.
313            #[inline]
314            fn saturating_from_fixed<F: Fixed>(src: F) -> Self {
315                FromFixed::from_fixed(src)
316            }
317
318            /// Converts a fixed-point number to a floating-point
319            /// number, wrapping if it does not fit.
320            ///
321            /// Rounding is to the nearest, with ties rounded to even.
322            #[inline]
323            fn wrapping_from_fixed<F: Fixed>(src: F) -> Self {
324                FromFixed::from_fixed(src)
325            }
326
327            /// Converts a fixed-point number to a floating-point number.
328            ///
329            /// Returns a [tuple] of the value and a [`bool`]
330            /// indicating whether an overflow has occurred. On
331            /// overflow, the wrapped value is returned.
332            ///
333            /// Rounding is to the nearest, with ties rounded to even.
334            #[inline]
335            fn overflowing_from_fixed<F: Fixed>(src: F) -> (Self, bool) {
336                (FromFixed::from_fixed(src), false)
337            }
338
339            /// Converts a fixed-point number to a floating-point
340            /// number, panicking if it does not fit.
341            ///
342            /// Rounding is to the nearest, with ties rounded to even.
343            ///
344            /// # Panics
345            ///
346            /// Panics if the value does not fit, even when debug
347            /// assertions are not enabled.
348            #[inline]
349            #[track_caller]
350            fn unwrapped_from_fixed<F: Fixed>(src: F) -> Self {
351                FromFixed::from_fixed(src)
352            }
353        }
354
355        impl ToFixed for $Float {
356            comment! {
357                "Converts a floating-point number to a fixed-point number.
358
359Rounding is to the nearest, with ties rounded to even.
360
361# Panics
362
363Panics if `self` is not [finite].
364
365When debug assertions are enabled, also panics if the value does not
366fit. When debug assertions are not enabled, the wrapped value can be
367returned, but it is not considered a breaking change if in the future
368it panics; if wrapping is required use [`wrapping_to_fixed`] instead.
369
370[`wrapping_to_fixed`]: ToFixed::wrapping_to_fixed
371[finite]: ", $link, "::is_finite
372";
373                #[inline]
374                #[track_caller]
375                fn to_fixed<F: Fixed>(self) -> F {
376                    let (wrapped, overflow) = ToFixed::overflowing_to_fixed(self);
377                    debug_assert!(!overflow, $overflows_fmt, $overflows_filt(self));
378                    wrapped
379                }
380            }
381
382            /// Converts a floating-point number to a fixed-point
383            /// number if it fits, otherwise returns [`None`].
384            ///
385            /// Rounding is to the nearest, with ties rounded to even.
386            #[inline]
387            fn checked_to_fixed<F: Fixed>(self) -> Option<F> {
388                let kind = float_helper::$Float::to_float_kind(self, F::FRAC_NBITS, F::INT_NBITS);
389                match kind {
390                    FloatKind::Finite { .. } => {
391                        let helper = FromFloatHelper { kind };
392                        match F::overflowing_from_float_helper(Private, helper) {
393                            (_, true) => None,
394                            (wrapped, false) => Some(wrapped),
395                        }
396                    }
397                    _ => None,
398                }
399            }
400
401            comment! {
402                "Converts a floating-point number to a fixed-point
403number, saturating if it does not fit.
404
405Rounding is to the nearest, with ties rounded to even.
406
407# Panics
408
409Panics if `self` is [NaN].
410
411[NaN]: ", $link, "::is_nan
412";
413                #[inline]
414                #[track_caller]
415                fn saturating_to_fixed<F: Fixed>(self) -> F {
416                    let kind =
417                        float_helper::$Float::to_float_kind(self, F::FRAC_NBITS, F::INT_NBITS);
418                    let helper = FromFloatHelper { kind };
419                    F::saturating_from_float_helper(Private, helper)
420                }
421            }
422
423            comment! {
424                "Converts a floating-point number to a fixed-point
425number, wrapping if it does not fit.
426
427Rounding is to the nearest, with ties rounded to even.
428
429# Panics
430
431Panics if `self` is not [finite].
432
433[finite]: ", $link, "::is_finite
434";
435                #[inline]
436                #[track_caller]
437                fn wrapping_to_fixed<F: Fixed>(self) -> F {
438                    let (wrapped, _) = ToFixed::overflowing_to_fixed(self);
439                    wrapped
440                }
441            }
442
443            comment! {
444            "Converts a floating-point number to a fixed-point number.
445
446Returns a [tuple] of the fixed-point number and a [`bool`] indicating
447whether an overflow has occurred. On overflow, the wrapped value is
448returned.
449
450Rounding is to the nearest, with ties rounded to even.
451
452# Panics
453
454Panics if `self` is not [finite].
455
456[finite]: ", $link, "::is_finite
457";
458                #[inline]
459                #[track_caller]
460                fn overflowing_to_fixed<F: Fixed>(self) -> (F, bool) {
461                    let kind =
462                        float_helper::$Float::to_float_kind(self, F::FRAC_NBITS, F::INT_NBITS);
463                    let helper = FromFloatHelper { kind };
464                    F::overflowing_from_float_helper(Private, helper)
465                }
466            }
467
468            comment! {
469                "Converts a floating-point number to a fixed-point
470number, panicking if it does not fit.
471
472Rounding is to the nearest, with ties rounded to even.
473
474# Panics
475
476Panics if `self` is not [finite] or if the value does not fit, even
477when debug assertions are not enabled.
478
479[finite]: ", $link, "::is_finite
480";
481                #[inline]
482                #[track_caller]
483                fn unwrapped_to_fixed<F: Fixed>(self) -> F {
484                    match ToFixed::overflowing_to_fixed(self) {
485                        (val, false) => val,
486                        (_, true) => panic!("overflow"),
487                    }
488                }
489            }
490        }
491    };
492}
493
494#[cfg(feature = "nightly-float")]
495impl_float! { f16, "f16", "f16::from_bits(0x{:04X}) overflows", |x: f16| x.to_bits() }
496impl_float! { half_f16, "half::f16", "{} overflows", |x| x }
497impl_float! { half_bf16, "half::bf16", "{} overflows", |x| x }
498impl_float! { f32, "f32", "{} overflows", |x| x }
499impl_float! { f64, "f64", "{} overflows", |x| x }
500#[cfg(feature = "nightly-float")]
501impl_float! { f128, "f128", "f128::from_bits(0x{:032X}) overflows", |x: f128| x.to_bits() }
502impl_float! { F128, "F128", "F128::from_bits(0x{:032X}) overflows", |x: F128| x.to_bits() }
503impl_float! { F128Bits, "f64", "F128Bits({}) overflows", |x: F128Bits| x.0 }