1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
//! 特定于平台的组装说明,以避免对具有 FPU 的架构进行中间舍入。 //! pub use fpu_precision::set_precision; // 在 x86 上,如果 SSE/SSE2 扩展不可用,则将 x87 FPU 用于浮动操作。 // x87 FPU 默认情况下以 80 位精度运行,这意味着运算将舍入到 80 位,从而在最终将值表示为时将发生双舍入 // // 32/64 位浮点值。为了克服这个问题,可以设置 FPU 控制字,以便以所需的精度执行计算。 // #[cfg(all(target_arch = "x86", not(target_feature = "sse2")))] mod fpu_precision { use core::mem::size_of; /// 一种结构体,用于保留 FPU 控制字的原始值,以便在丢弃该结构体时可以将其恢复。 /// /// /// x87 FPU 是一个 16 位寄存器,其字段如下: /// /// | 12-15 | 10-11 | 8-9 | 6-7 | 5 | 4 | 3 | 2 | 1 | 0 | /// |------:|------:|----:|----:|---:|---:|---:|---:|---:|---:| /// | | RC | PC | | PM | UM | OM | ZM | DM | IM | /// /// IA-32 体系结构软件开发人员手册 (第 1 卷) 中提供了所有字段的文档。 /// /// 与以下代码相关的唯一字段是 PC,Precision Control。 /// 该字段确定 FPU 执行的操作的精度。 /// 可以设置为: /// - 0b00,单精度,即 32 位 /// - 0b10,双精度,即 64 位 /// - 0b11,双精度扩展精度,即 80 位 (默认状态) 0b01 值是保留的,不应使用。 /// pub struct FPUControlWord(u16); fn set_cw(cw: u16) { // SAFETY: `fldcw` 指令已通过审核,可以与任何 `u16` 一起正常使用 // unsafe { asm!( "fldcw word ptr [{}]", in(reg) &cw, options(nostack), ) } } /// 将 FPU 的 precision 字段设置为 `T` 并返回 `FPUControlWord`。 pub fn set_precision<T>() -> FPUControlWord { let mut cw = 0_u16; // 计算适用于 `T` 的 Precision Control 字段的值。 let cw_precision = match size_of::<T>() { 4 => 0x0000, // 32 位 8 => 0x0200, // 64 位 _ => 0x0300, // 默认为 80 位 }; // 丢弃 `FPUControlWord` 结构体时,获取控制字的原始值以在以后还原它 // // SAFETY: `fnstcw` 指令已通过审核,可以与任何 `u16` 一起正常使用 // unsafe { asm!( "fnstcw word ptr [{}]", in(reg) &mut cw, options(nostack), ) } // 将控制字设置为所需的精度。 // 这可以通过掩盖旧的精度 (位 8 和 9,0x300) 并将其替换为上面计算的精度标志来实现。 set_cw((cw & 0xFCFF) | cw_precision); FPUControlWord(cw) } impl Drop for FPUControlWord { fn drop(&mut self) { set_cw(self.0) } } } // 在大多数体系结构中,浮点运算具有显式的位大小,因此计算的精度取决于每个运算。 // #[cfg(any(not(target_arch = "x86"), target_feature = "sse2"))] mod fpu_precision { pub fn set_precision<T>() {} }