Coverage Report

Created: 2025-08-28 06:17

/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
}