| //! Support for rounding directions and status flags as specified by IEEE 754. | |
| //! | |
| //! Rust does not support the floating point environment so rounding mode is passed as an argument | |
| //! and status flags are returned as part of the result. There is currently not much support for | |
| //! this; most existing ports from musl use a form of `force_eval!` to raise exceptions, but this | |
| //! has no side effects in Rust. Further, correct behavior relies on elementary operations making | |
| //! use of the correct rounding and raising relevant exceptions, which is not the case for Rust. | |
| //! | |
| //! This module exists so no functionality is lost when porting algorithms that respect floating | |
| //! point environment, and so that some functionality may be tested (that which does not rely on | |
| //! side effects from elementary operations). Full support would require wrappers around basic | |
| //! operations, but there is no plan to add this at the current time. | |
| /// A value combined with a floating point status. | |
| pub struct FpResult<T> { | |
| pub val: T, | |
| #[cfg_attr(not(feature = "unstable-public-internals"), allow(dead_code))] | |
| pub status: Status, | |
| } | |
| impl<T> FpResult<T> { | |
| pub fn new(val: T, status: Status) -> Self { | |
| Self { val, status } | |
| } | |
| /// Return `val` with `Status::OK`. | |
| pub fn ok(val: T) -> Self { | |
| Self { | |
| val, | |
| status: Status::OK, | |
| } | |
| } | |
| } | |
| /// IEEE 754 rounding mode, excluding the optional `roundTiesToAway` version of nearest. | |
| /// | |
| /// Integer representation comes from what CORE-MATH uses for indexing. | |
| #[cfg_attr(not(feature = "unstable-public-internals"), allow(dead_code))] | |
| #[derive(Clone, Copy, Debug, PartialEq)] | |
| pub enum Round { | |
| /// IEEE 754 nearest, `roundTiesToEven`. | |
| Nearest = 0, | |
| /// IEEE 754 `roundTowardNegative`. | |
| Negative = 1, | |
| /// IEEE 754 `roundTowardPositive`. | |
| Positive = 2, | |
| /// IEEE 754 `roundTowardZero`. | |
| Zero = 3, | |
| } | |
| /// IEEE 754 exception status flags. | |
| #[derive(Clone, Copy, PartialEq, Eq)] | |
| pub struct Status(u8); | |
| impl Status { | |
| /* Note that if we ever store/load this to/from floating point control status registers, it | |
| * may be worth making these values platform-dependent to line up with register layout | |
| * to avoid bit swapping. For the time being, this isn't a concern. */ | |
| /// Default status indicating no errors. | |
| pub const OK: Self = Self(0); | |
| /// No definable result. | |
| /// | |
| /// Includes: | |
| /// - Any ops on sNaN, with a few exceptions. | |
| /// - `0 * inf`, `inf * 0`. | |
| /// - `fma(0, inf, c)` or `fma(inf, 0, c)`, possibly excluding `c = qNaN`. | |
| /// - `+inf + -inf` and similar (includes subtraction and fma). | |
| /// - `0.0 / 0.0`, `inf / inf` | |
| /// - `remainder(x, y)` if `y == 0.0` or `x == inf`, and neither is NaN. | |
| /// - `sqrt(x)` with `x < 0.0`. | |
| pub const INVALID: Self = Self(1); | |
| /// Division by zero. | |
| /// | |
| /// The default result for division is +/-inf based on operand sign. For `logB`, the default | |
| /// result is -inf. | |
| /// `x / y` when `x != 0.0` and `y == 0.0`, | |
| #[cfg_attr(not(feature = "unstable-public-internals"), allow(dead_code))] | |
| pub const DIVIDE_BY_ZERO: Self = Self(1 << 1); | |
| /// The result exceeds the maximum finite value. | |
| /// | |
| /// The default result depends on rounding mode. `Nearest*` rounds to +/- infinity, sign based | |
| /// on the intermediate result. `Zero` rounds to the signed maximum finite. `Positive` and | |
| /// `Negative` round to signed maximum finite in one direction, signed infinity in the other. | |
| #[cfg_attr(not(feature = "unstable-public-internals"), allow(dead_code))] | |
| pub const OVERFLOW: Self = Self(1 << 2); | |
| /// The result is subnormal and lost precision. | |
| pub const UNDERFLOW: Self = Self(1 << 3); | |
| /// The finite-precision result does not match that of infinite precision, and the reason | |
| /// is not represented by one of the other flags. | |
| pub const INEXACT: Self = Self(1 << 4); | |
| /// True if `UNDERFLOW` is set. | |
| #[cfg_attr(not(feature = "unstable-public-internals"), allow(dead_code))] | |
| pub const fn underflow(self) -> bool { | |
| self.0 & Self::UNDERFLOW.0 != 0 | |
| } | |
| /// True if `OVERFLOW` is set. | |
| #[cfg_attr(not(feature = "unstable-public-internals"), allow(dead_code))] | |
| pub const fn overflow(self) -> bool { | |
| self.0 & Self::OVERFLOW.0 != 0 | |
| } | |
| pub fn set_underflow(&mut self, val: bool) { | |
| self.set_flag(val, Self::UNDERFLOW); | |
| } | |
| /// True if `INEXACT` is set. | |
| pub const fn inexact(self) -> bool { | |
| self.0 & Self::INEXACT.0 != 0 | |
| } | |
| pub fn set_inexact(&mut self, val: bool) { | |
| self.set_flag(val, Self::INEXACT); | |
| } | |
| fn set_flag(&mut self, val: bool, mask: Self) { | |
| if val { | |
| self.0 |= mask.0; | |
| } else { | |
| self.0 &= !mask.0; | |
| } | |
| } | |
| pub(crate) const fn with(self, rhs: Self) -> Self { | |
| Self(self.0 | rhs.0) | |
| } | |
| } | |
| #[cfg(any(test, feature = "unstable-public-internals"))] | |
| impl core::fmt::Debug for Status { | |
| fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | |
| // shift -> flag map | |
| let names = &[ | |
| "INVALID", | |
| "DIVIDE_BY_ZERO", | |
| "OVERFLOW", | |
| "UNDERFLOW", | |
| "INEXACT", | |
| ]; | |
| write!(f, "Status(")?; | |
| let mut any = false; | |
| for shift in 0..u8::BITS { | |
| if self.0 & (1 << shift) != 0 { | |
| if any { | |
| write!(f, " | ")?; | |
| } | |
| match names.get(shift as usize) { | |
| Some(name) => write!(f, "{name}")?, | |
| None => write!(f, "UNKNOWN(1 << {shift})")?, | |
| } | |
| any = true; | |
| } | |
| } | |
| if !any { | |
| write!(f, "OK")?; | |
| } | |
| write!(f, ")")?; | |
| Ok(()) | |
| } | |
| } |