| 1 | //! Support for rounding directions and status flags as specified by IEEE 754. |
| 2 | //! |
| 3 | //! Rust does not support the floating point environment so rounding mode is passed as an argument |
| 4 | //! and status flags are returned as part of the result. There is currently not much support for |
| 5 | //! this; most existing ports from musl use a form of `force_eval!` to raise exceptions, but this |
| 6 | //! has no side effects in Rust. Further, correct behavior relies on elementary operations making |
| 7 | //! use of the correct rounding and raising relevant exceptions, which is not the case for Rust. |
| 8 | //! |
| 9 | //! This module exists so no functionality is lost when porting algorithms that respect floating |
| 10 | //! point environment, and so that some functionality may be tested (that which does not rely on |
| 11 | //! side effects from elementary operations). Full support would require wrappers around basic |
| 12 | //! operations, but there is no plan to add this at the current time. |
| 13 | |
| 14 | /// A value combined with a floating point status. |
| 15 | pub struct FpResult<T> { |
| 16 | pub val: T, |
| 17 | #[cfg_attr (not(feature = "unstable-public-internals" ), allow(dead_code))] |
| 18 | pub status: Status, |
| 19 | } |
| 20 | |
| 21 | impl<T> FpResult<T> { |
| 22 | pub fn new(val: T, status: Status) -> Self { |
| 23 | Self { val, status } |
| 24 | } |
| 25 | |
| 26 | /// Return `val` with `Status::OK`. |
| 27 | pub fn ok(val: T) -> Self { |
| 28 | Self { |
| 29 | val, |
| 30 | status: Status::OK, |
| 31 | } |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | /// IEEE 754 rounding mode, excluding the optional `roundTiesToAway` version of nearest. |
| 36 | /// |
| 37 | /// Integer representation comes from what CORE-MATH uses for indexing. |
| 38 | #[cfg_attr (not(feature = "unstable-public-internals" ), allow(dead_code))] |
| 39 | #[derive (Clone, Copy, Debug, PartialEq)] |
| 40 | pub enum Round { |
| 41 | /// IEEE 754 nearest, `roundTiesToEven`. |
| 42 | Nearest = 0, |
| 43 | /// IEEE 754 `roundTowardNegative`. |
| 44 | Negative = 1, |
| 45 | /// IEEE 754 `roundTowardPositive`. |
| 46 | Positive = 2, |
| 47 | /// IEEE 754 `roundTowardZero`. |
| 48 | Zero = 3, |
| 49 | } |
| 50 | |
| 51 | /// IEEE 754 exception status flags. |
| 52 | #[derive (Clone, Copy, Debug, PartialEq, Eq)] |
| 53 | pub struct Status(u8); |
| 54 | |
| 55 | impl Status { |
| 56 | /// Default status indicating no errors. |
| 57 | pub const OK: Self = Self(0); |
| 58 | |
| 59 | /// No definable result. |
| 60 | /// |
| 61 | /// Includes: |
| 62 | /// - Any ops on sNaN, with a few exceptions. |
| 63 | /// - `0 * inf`, `inf * 0`. |
| 64 | /// - `fma(0, inf, c)` or `fma(inf, 0, c)`, possibly excluding `c = qNaN`. |
| 65 | /// - `+inf + -inf` and similar (includes subtraction and fma). |
| 66 | /// - `0.0 / 0.0`, `inf / inf` |
| 67 | /// - `remainder(x, y)` if `y == 0.0` or `x == inf`, and neither is NaN. |
| 68 | /// - `sqrt(x)` with `x < 0.0`. |
| 69 | pub const INVALID: Self = Self(1); |
| 70 | |
| 71 | /// Division by zero. |
| 72 | /// |
| 73 | /// The default result for division is +/-inf based on operand sign. For `logB`, the default |
| 74 | /// result is -inf. |
| 75 | /// `x / y` when `x != 0.0` and `y == 0.0`, |
| 76 | #[cfg_attr (not(feature = "unstable-public-internals" ), allow(dead_code))] |
| 77 | pub const DIVIDE_BY_ZERO: Self = Self(1 << 2); |
| 78 | |
| 79 | /// The result exceeds the maximum finite value. |
| 80 | /// |
| 81 | /// The default result depends on rounding mode. `Nearest*` rounds to +/- infinity, sign based |
| 82 | /// on the intermediate result. `Zero` rounds to the signed maximum finite. `Positive` and |
| 83 | /// `Negative` round to signed maximum finite in one direction, signed infinity in the other. |
| 84 | #[cfg_attr (not(feature = "unstable-public-internals" ), allow(dead_code))] |
| 85 | pub const OVERFLOW: Self = Self(1 << 3); |
| 86 | |
| 87 | /// The result is subnormal and lost precision. |
| 88 | pub const UNDERFLOW: Self = Self(1 << 4); |
| 89 | |
| 90 | /// The finite-precision result does not match that of infinite precision, and the reason |
| 91 | /// is not represented by one of the other flags. |
| 92 | pub const INEXACT: Self = Self(1 << 5); |
| 93 | |
| 94 | /// True if `UNDERFLOW` is set. |
| 95 | #[cfg_attr (not(feature = "unstable-public-internals" ), allow(dead_code))] |
| 96 | pub const fn underflow(self) -> bool { |
| 97 | self.0 & Self::UNDERFLOW.0 != 0 |
| 98 | } |
| 99 | |
| 100 | /// True if `OVERFLOW` is set. |
| 101 | #[cfg_attr (not(feature = "unstable-public-internals" ), allow(dead_code))] |
| 102 | pub const fn overflow(self) -> bool { |
| 103 | self.0 & Self::OVERFLOW.0 != 0 |
| 104 | } |
| 105 | |
| 106 | pub fn set_underflow(&mut self, val: bool) { |
| 107 | self.set_flag(val, Self::UNDERFLOW); |
| 108 | } |
| 109 | |
| 110 | /// True if `INEXACT` is set. |
| 111 | pub const fn inexact(self) -> bool { |
| 112 | self.0 & Self::INEXACT.0 != 0 |
| 113 | } |
| 114 | |
| 115 | pub fn set_inexact(&mut self, val: bool) { |
| 116 | self.set_flag(val, Self::INEXACT); |
| 117 | } |
| 118 | |
| 119 | fn set_flag(&mut self, val: bool, mask: Self) { |
| 120 | if val { |
| 121 | self.0 |= mask.0; |
| 122 | } else { |
| 123 | self.0 &= !mask.0; |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | pub(crate) const fn with(self, rhs: Self) -> Self { |
| 128 | Self(self.0 | rhs.0) |
| 129 | } |
| 130 | } |
| 131 | |