/src/rust-lexical/lexical-parse-float/src/fpu.rs
Line | Count | Source |
1 | | //! Platform-specific, assembly instructions to avoid |
2 | | //! intermediate rounding on architectures with FPUs. |
3 | | //! |
4 | | //! This is adapted from the implementation in the Rust core library, |
5 | | //! the original implementation can be [here](https://github.com/rust-lang/rust/blob/master/library/core/src/num/dec2flt/fpu.rs). |
6 | | //! |
7 | | //! It is therefore also subject to a Apache2.0/MIT license. |
8 | | |
9 | | #![doc(hidden)] |
10 | | |
11 | | pub use fpu_precision::set_precision; |
12 | | |
13 | | // On x86, the x87 FPU is used for float operations if the SSE/SSE2 extensions |
14 | | // are not available. The x87 FPU operates with 80 bits of precision by default, |
15 | | // which means that operations will round to 80 bits causing double rounding to |
16 | | // happen when values are eventually represented as 32/64 bit float values. To |
17 | | // overcome this, the FPU control word can be set so that the computations are |
18 | | // performed in the desired precision. |
19 | | #[cfg(all(target_arch = "x86", not(target_feature = "sse2")))] |
20 | | mod fpu_precision { |
21 | | // We only support the latest nightly, which is 1.59+. |
22 | | // The `asm!` macro was stabilized in 1.59.0. |
23 | | use core::arch::asm; |
24 | | use core::mem::size_of; |
25 | | |
26 | | /// A structure used to preserve the original value of the FPU control word, |
27 | | /// so that it can be restored when the structure is dropped. |
28 | | /// |
29 | | /// The x87 FPU is a 16-bits register whose fields are as follows: |
30 | | /// |
31 | | /// | 12-15 | 10-11 | 8-9 | 6-7 | 5 | 4 | 3 | 2 | 1 | 0 | |
32 | | /// |------:|------:|----:|----:|---:|---:|---:|---:|---:|---:| |
33 | | /// | | RC | PC | | PM | UM | OM | ZM | DM | IM | |
34 | | /// |
35 | | /// The documentation for all of the fields is available in the IA-32 |
36 | | /// Architectures Software Developer's Manual (Volume 1). |
37 | | /// |
38 | | /// The only field which is relevant for the following code is PC, Precision |
39 | | /// Control. This field determines the precision of the operations |
40 | | /// performed by the FPU. It can be set to: |
41 | | /// - 0b00, single precision i.e., 32-bits |
42 | | /// - 0b10, double precision i.e., 64-bits |
43 | | /// - 0b11, double extended precision i.e., 80-bits (default state) |
44 | | /// The 0b01 value is reserved and should not be used. |
45 | | pub struct FPUControlWord(u16); |
46 | | |
47 | | fn set_cw(cw: u16) { |
48 | | // SAFETY: the `fldcw` instruction has been audited to be able to work correctly |
49 | | // with any `u16` |
50 | | unsafe { |
51 | | asm!( |
52 | | "fldcw word ptr [{}]", |
53 | | in(reg) &cw, |
54 | | options(nostack), |
55 | | ) |
56 | | } |
57 | | } |
58 | | |
59 | | /// Sets the precision field of the FPU to `T` and returns a |
60 | | /// `FPUControlWord`. |
61 | | pub fn set_precision<T>() -> FPUControlWord { |
62 | | let mut cw = 0_u16; |
63 | | |
64 | | // Compute the value for the Precision Control field that is appropriate for |
65 | | // `T`. |
66 | | let cw_precision = match size_of::<T>() { |
67 | | 4 => 0x0000, // 32 bits |
68 | | 8 => 0x0200, // 64 bits |
69 | | _ => 0x0300, // default, 80 bits |
70 | | }; |
71 | | |
72 | | // Get the original value of the control word to restore it later, when the |
73 | | // `FPUControlWord` structure is dropped |
74 | | // SAFETY: the `fnstcw` instruction has been audited to be able to work |
75 | | // correctly with any `u16` |
76 | | unsafe { |
77 | | asm!( |
78 | | "fnstcw word ptr [{}]", |
79 | | in(reg) &mut cw, |
80 | | options(nostack), |
81 | | ) |
82 | | } |
83 | | |
84 | | // Set the control word to the desired precision. This is achieved by masking |
85 | | // away the old precision (bits 8 and 9, 0x300) and replacing it with |
86 | | // the precision flag computed above. |
87 | | set_cw((cw & 0xFCFF) | cw_precision); |
88 | | |
89 | | FPUControlWord(cw) |
90 | | } |
91 | | |
92 | | impl Drop for FPUControlWord { |
93 | | fn drop(&mut self) { |
94 | | set_cw(self.0) |
95 | | } |
96 | | } |
97 | | } |
98 | | |
99 | | // In most architectures, floating point operations have an explicit bit size, |
100 | | // therefore the precision of the computation is determined on a per-operation |
101 | | // basis. |
102 | | #[cfg(any(not(target_arch = "x86"), target_feature = "sse2"))] |
103 | | mod fpu_precision { |
104 | | #[inline] |
105 | 4.51k | pub const fn set_precision<T>() { |
106 | 4.51k | } lexical_parse_float::fpu::fpu_precision::set_precision::<f32> Line | Count | Source | 105 | 1.81k | pub const fn set_precision<T>() { | 106 | 1.81k | } |
Unexecuted instantiation: lexical_parse_float::fpu::fpu_precision::set_precision::<_> lexical_parse_float::fpu::fpu_precision::set_precision::<f64> Line | Count | Source | 105 | 2.70k | pub const fn set_precision<T>() { | 106 | 2.70k | } |
|
107 | | } |