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 { val, status: Status::OK } |
29 | } |
30 | } |
31 | |
32 | /// IEEE 754 rounding mode, excluding the optional `roundTiesToAway` version of nearest. |
33 | /// |
34 | /// Integer representation comes from what CORE-MATH uses for indexing. |
35 | #[cfg_attr (not(feature = "unstable-public-internals" ), allow(dead_code))] |
36 | #[derive (Clone, Copy, Debug, PartialEq)] |
37 | pub enum Round { |
38 | /// IEEE 754 nearest, `roundTiesToEven`. |
39 | Nearest = 0, |
40 | /// IEEE 754 `roundTowardNegative`. |
41 | Negative = 1, |
42 | /// IEEE 754 `roundTowardPositive`. |
43 | Positive = 2, |
44 | /// IEEE 754 `roundTowardZero`. |
45 | Zero = 3, |
46 | } |
47 | |
48 | /// IEEE 754 exception status flags. |
49 | #[derive (Clone, Copy, Debug, PartialEq)] |
50 | pub struct Status(u8); |
51 | |
52 | impl Status { |
53 | /// Default status indicating no errors. |
54 | pub const OK: Self = Self(0); |
55 | |
56 | /// No definable result. |
57 | /// |
58 | /// Includes: |
59 | /// - Any ops on sNaN, with a few exceptions. |
60 | /// - `0 * inf`, `inf * 0`. |
61 | /// - `fma(0, inf, c)` or `fma(inf, 0, c)`, possibly excluding `c = qNaN`. |
62 | /// - `+inf + -inf` and similar (includes subtraction and fma). |
63 | /// - `0.0 / 0.0`, `inf / inf` |
64 | /// - `remainder(x, y)` if `y == 0.0` or `x == inf`, and neither is NaN. |
65 | /// - `sqrt(x)` with `x < 0.0`. |
66 | pub const INVALID: Self = Self(1); |
67 | |
68 | /// Division by zero. |
69 | /// |
70 | /// The default result for division is +/-inf based on operand sign. For `logB`, the default |
71 | /// result is -inf. |
72 | /// `x / y` when `x != 0.0` and `y == 0.0`, |
73 | |
74 | #[cfg_attr (not(feature = "unstable-public-internals" ), allow(dead_code))] |
75 | pub const DIVIDE_BY_ZERO: Self = Self(1 << 2); |
76 | |
77 | /// The result exceeds the maximum finite value. |
78 | /// |
79 | /// The default result depends on rounding mode. `Nearest*` rounds to +/- infinity, sign based |
80 | /// on the intermediate result. `Zero` rounds to the signed maximum finite. `Positive` and |
81 | /// `Negative` round to signed maximum finite in one direction, signed infinity in the other. |
82 | #[cfg_attr (not(feature = "unstable-public-internals" ), allow(dead_code))] |
83 | pub const OVERFLOW: Self = Self(1 << 3); |
84 | |
85 | /// The result is subnormal and lost precision. |
86 | pub const UNDERFLOW: Self = Self(1 << 4); |
87 | |
88 | /// The finite-precision result does not match that of infinite precision, and the reason |
89 | /// is not represented by one of the other flags. |
90 | pub const INEXACT: Self = Self(1 << 5); |
91 | |
92 | /// True if `UNDERFLOW` is set. |
93 | #[cfg_attr (not(feature = "unstable-public-internals" ), allow(dead_code))] |
94 | pub fn underflow(self) -> bool { |
95 | self.0 & Self::UNDERFLOW.0 != 0 |
96 | } |
97 | |
98 | pub fn set_underflow(&mut self, val: bool) { |
99 | self.set_flag(val, Self::UNDERFLOW); |
100 | } |
101 | |
102 | /// True if `INEXACT` is set. |
103 | pub fn inexact(self) -> bool { |
104 | self.0 & Self::INEXACT.0 != 0 |
105 | } |
106 | |
107 | pub fn set_inexact(&mut self, val: bool) { |
108 | self.set_flag(val, Self::INEXACT); |
109 | } |
110 | |
111 | fn set_flag(&mut self, val: bool, mask: Self) { |
112 | if val { |
113 | self.0 |= mask.0; |
114 | } else { |
115 | self.0 &= !mask.0; |
116 | } |
117 | } |
118 | } |
119 | |