Skip to main content

machina_softfloat/ops/
add.rs

1// SPDX-License-Identifier: MIT
2// IEEE 754 floating-point addition and subtraction.
3
4use crate::env::{FloatEnv, RoundMode};
5use crate::parts::{nan_propagate, round_pack, unpack, FloatClass, FloatParts};
6use crate::types::{
7    BFloat16, Float128, Float16, Float32, Float64, FloatFormat, FloatX80,
8};
9
10/// Floating-point addition: a + b.
11pub fn add<F: FloatFormat>(a: F, b: F, env: &mut FloatEnv) -> F {
12    let pa = unpack::<F>(a);
13    let pb = unpack::<F>(b);
14    add_parts::<F>(&pa, &pb, env)
15}
16
17/// Floating-point subtraction: a - b.
18pub fn sub<F: FloatFormat>(a: F, b: F, env: &mut FloatEnv) -> F {
19    let pa = unpack::<F>(a);
20    let mut pb = unpack::<F>(b);
21    pb.sign = !pb.sign;
22    add_parts::<F>(&pa, &pb, env)
23}
24
25fn add_parts<F: FloatFormat>(
26    a: &FloatParts,
27    b: &FloatParts,
28    env: &mut FloatEnv,
29) -> F {
30    // NaN propagation
31    if a.is_nan() || b.is_nan() {
32        let r = nan_propagate(a, b, env);
33        let mut r = r;
34        return round_pack::<F>(&mut r, env);
35    }
36
37    // Inf handling
38    if a.cls == FloatClass::Inf {
39        if b.cls == FloatClass::Inf {
40            if a.sign != b.sign {
41                // Inf + (-Inf) = NaN, INVALID
42                return crate::parts::return_nan::<F>(env);
43            }
44            // Same-sign infinities.
45            let mut r = *a;
46            return round_pack::<F>(&mut r, env);
47        }
48        let mut r = *a;
49        return round_pack::<F>(&mut r, env);
50    }
51    if b.cls == FloatClass::Inf {
52        let mut r = *b;
53        return round_pack::<F>(&mut r, env);
54    }
55
56    // Zero handling
57    if a.cls == FloatClass::Zero {
58        if b.cls == FloatClass::Zero {
59            // 0 + 0: sign follows IEEE rules.
60            let sign = if a.sign == b.sign {
61                a.sign
62            } else {
63                env.round_mode() == RoundMode::Down
64            };
65            let mut r = FloatParts {
66                sign,
67                exp: 0,
68                frac: 0,
69                cls: FloatClass::Zero,
70            };
71            return round_pack::<F>(&mut r, env);
72        }
73        let mut r = *b;
74        return round_pack::<F>(&mut r, env);
75    }
76    if b.cls == FloatClass::Zero {
77        let mut r = *a;
78        return round_pack::<F>(&mut r, env);
79    }
80
81    // Both are Normal.
82    if a.sign == b.sign {
83        add_normal::<F>(a, b, env)
84    } else {
85        sub_normal::<F>(a, b, env)
86    }
87}
88
89/// Add two same-sign normals.
90fn add_normal<F: FloatFormat>(
91    a: &FloatParts,
92    b: &FloatParts,
93    env: &mut FloatEnv,
94) -> F {
95    let (mut big, mut small) = if a.exp >= b.exp { (*a, *b) } else { (*b, *a) };
96
97    let exp_diff = (big.exp - small.exp) as u32;
98
99    // Align the smaller operand's fraction.
100    if exp_diff > 0 {
101        if exp_diff >= 128 {
102            // small is entirely in the sticky region.
103            small.frac = if small.frac != 0 { 1 } else { 0 };
104        } else {
105            let sticky = if small.frac & ((1u128 << exp_diff) - 1) != 0 {
106                1u128
107            } else {
108                0
109            };
110            small.frac = (small.frac >> exp_diff) | sticky;
111        }
112    }
113
114    big.frac = big.frac.wrapping_add(small.frac);
115    // The addition may overflow the integer bit position;
116    // round_pack handles normalization.
117    let mut result = FloatParts {
118        sign: big.sign,
119        exp: big.exp,
120        frac: big.frac,
121        cls: FloatClass::Normal,
122    };
123    round_pack::<F>(&mut result, env)
124}
125
126/// Subtract two normals with different signs
127/// (effective subtraction).
128fn sub_normal<F: FloatFormat>(
129    a: &FloatParts,
130    b: &FloatParts,
131    env: &mut FloatEnv,
132) -> F {
133    // Determine which has larger magnitude.
134    let (big, small, result_sign) = if a.exp > b.exp {
135        (*a, *b, a.sign)
136    } else if a.exp < b.exp {
137        (*b, *a, b.sign)
138    } else if a.frac > b.frac {
139        (*a, *b, a.sign)
140    } else if a.frac < b.frac {
141        (*b, *a, b.sign)
142    } else {
143        // Exact cancellation: result is +0 (or -0
144        // in round-down mode).
145        let sign = env.round_mode() == RoundMode::Down;
146        let mut r = FloatParts {
147            sign,
148            exp: 0,
149            frac: 0,
150            cls: FloatClass::Zero,
151        };
152        return round_pack::<F>(&mut r, env);
153    };
154
155    let exp_diff = (big.exp - small.exp) as u32;
156
157    let mut small_frac = small.frac;
158    if exp_diff > 0 {
159        if exp_diff >= 128 {
160            small_frac = if small_frac != 0 { 1 } else { 0 };
161        } else {
162            let sticky = if small_frac & ((1u128 << exp_diff) - 1) != 0 {
163                1u128
164            } else {
165                0
166            };
167            small_frac = (small_frac >> exp_diff) | sticky;
168        }
169    }
170
171    let frac = big.frac.wrapping_sub(small_frac);
172
173    if frac == 0 {
174        let sign = env.round_mode() == RoundMode::Down;
175        let mut r = FloatParts {
176            sign,
177            exp: 0,
178            frac: 0,
179            cls: FloatClass::Zero,
180        };
181        return round_pack::<F>(&mut r, env);
182    }
183
184    let mut result = FloatParts {
185        sign: result_sign,
186        exp: big.exp,
187        frac,
188        cls: FloatClass::Normal,
189    };
190    round_pack::<F>(&mut result, env)
191}
192
193// ---------------------------------------------------------------
194// Convenience methods
195// ---------------------------------------------------------------
196
197macro_rules! impl_add_sub {
198    ($ty:ty) => {
199        impl $ty {
200            pub fn add(self, other: Self, env: &mut FloatEnv) -> Self {
201                add::<Self>(self, other, env)
202            }
203            pub fn sub(self, other: Self, env: &mut FloatEnv) -> Self {
204                sub::<Self>(self, other, env)
205            }
206        }
207    };
208}
209
210impl_add_sub!(Float16);
211impl_add_sub!(BFloat16);
212impl_add_sub!(Float32);
213impl_add_sub!(Float64);
214impl_add_sub!(Float128);
215impl_add_sub!(FloatX80);