libm/math/support/
env.rs

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.
15pub struct FpResult<T> {
16    pub val: T,
17    #[cfg_attr(not(feature = "unstable-public-internals"), allow(dead_code))]
18    pub status: Status,
19}
20
21impl<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)]
40pub 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)]
53pub struct Status(u8);
54
55impl 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}