Skip to main content

machina_softfloat/ops/
misc.rs

1// SPDX-License-Identifier: MIT
2// Miscellaneous floating-point operations: abs, neg, scalbn,
3// classify.
4
5use crate::env::FloatEnv;
6use crate::parts::{round_pack, unpack, FloatClass};
7use crate::types::{
8    BFloat16, BitOps, Float128, Float16, Float32, Float64, FloatFormat,
9    FloatX80,
10};
11
12/// Absolute value: clear the sign bit.
13pub fn abs<F: FloatFormat>(a: F) -> F {
14    let bits = a.to_bits().to_u128();
15    let frac_total = if F::HAS_EXPLICIT_INT {
16        F::FRAC_BITS + 1
17    } else {
18        F::FRAC_BITS
19    };
20    let sign_pos = frac_total + F::EXP_BITS;
21    let mask = !(1u128 << sign_pos);
22    let result = bits & mask;
23    F::from_bits(<F::Bits as BitOps>::from_u128(result))
24}
25
26/// Negate: flip the sign bit.
27pub fn neg<F: FloatFormat>(a: F) -> F {
28    let bits = a.to_bits().to_u128();
29    let frac_total = if F::HAS_EXPLICIT_INT {
30        F::FRAC_BITS + 1
31    } else {
32        F::FRAC_BITS
33    };
34    let sign_pos = frac_total + F::EXP_BITS;
35    let result = bits ^ (1u128 << sign_pos);
36    F::from_bits(<F::Bits as BitOps>::from_u128(result))
37}
38
39/// Scale by power of 2: a * 2^n.
40/// Does not raise INEXACT (unlike multiplication by 2^n).
41pub fn scalbn<F: FloatFormat>(a: F, n: i32, env: &mut FloatEnv) -> F {
42    let mut parts = unpack::<F>(a);
43
44    match parts.cls {
45        FloatClass::Normal => {
46            parts.exp += n;
47            round_pack::<F>(&mut parts, env)
48        }
49        FloatClass::QNaN | FloatClass::SNaN => {
50            let mut r = crate::parts::nan_propagate_one(&parts, env);
51            round_pack::<F>(&mut r, env)
52        }
53        _ => {
54            // Zero and Inf are unchanged.
55            round_pack::<F>(&mut parts, env)
56        }
57    }
58}
59
60/// IEEE 754 classification.
61pub fn classify<F: FloatFormat>(a: F) -> FloatClass {
62    let parts = unpack::<F>(a);
63    parts.cls
64}
65
66/// Check if the value is a signaling NaN.
67pub fn is_signaling_nan<F: FloatFormat>(a: F) -> bool {
68    let parts = unpack::<F>(a);
69    parts.cls == FloatClass::SNaN
70}
71
72/// Check if the value is a quiet NaN.
73pub fn is_quiet_nan<F: FloatFormat>(a: F) -> bool {
74    let parts = unpack::<F>(a);
75    parts.cls == FloatClass::QNaN
76}
77
78/// Check if the value is subnormal (unpacks to Normal with
79/// biased_exp == 0 in the original encoding, but our unpack
80/// normalizes subnormals; detect via original exponent range).
81pub fn is_subnormal<F: FloatFormat>(a: F) -> bool {
82    let bits = a.to_bits().to_u128();
83    let frac_total = if F::HAS_EXPLICIT_INT {
84        F::FRAC_BITS + 1
85    } else {
86        F::FRAC_BITS
87    };
88    let exp_mask = (1u128 << F::EXP_BITS) - 1;
89    let raw_exp = (bits >> frac_total) & exp_mask;
90    let frac_mask = (1u128 << frac_total) - 1;
91    let raw_frac = bits & frac_mask;
92
93    raw_exp == 0 && raw_frac != 0
94}
95
96// ---------------------------------------------------------------
97// Convenience methods
98// ---------------------------------------------------------------
99
100macro_rules! impl_misc {
101    ($ty:ty) => {
102        impl $ty {
103            pub fn abs(self) -> Self {
104                abs::<Self>(self)
105            }
106            #[allow(clippy::should_implement_trait)]
107            pub fn neg(self) -> Self {
108                neg::<Self>(self)
109            }
110            pub fn scalbn(self, n: i32, env: &mut FloatEnv) -> Self {
111                scalbn::<Self>(self, n, env)
112            }
113            pub fn classify(self) -> FloatClass {
114                classify::<Self>(self)
115            }
116        }
117    };
118}
119
120impl_misc!(Float16);
121impl_misc!(BFloat16);
122impl_misc!(Float32);
123impl_misc!(Float64);
124impl_misc!(Float128);
125impl_misc!(FloatX80);