Skip to main content

machina_softfloat/ops/
convert.rs

1// SPDX-License-Identifier: MIT
2// IEEE 754 floating-point conversions (float-to-float,
3// float-to-int, int-to-float).
4
5use crate::env::{ExcFlags, FloatEnv, RoundMode};
6use crate::parts::{round_pack, unpack, FloatClass, FloatParts};
7use crate::types::{
8    BFloat16, BitOps, Float128, Float16, Float32, Float64, FloatFormat,
9    FloatX80,
10};
11
12const INT_BIT: u32 = 126;
13
14// ---------------------------------------------------------------
15// Float-to-float conversion
16// ---------------------------------------------------------------
17
18/// Convert from format F to format G (widen or narrow).
19pub fn convert<F: FloatFormat, G: FloatFormat>(a: F, env: &mut FloatEnv) -> G {
20    let mut parts = unpack::<F>(a);
21    round_pack::<G>(&mut parts, env)
22}
23
24// ---------------------------------------------------------------
25// Float-to-integer (internal helper)
26// ---------------------------------------------------------------
27
28/// Extract the magnitude as u128, with rounding applied,
29/// plus inexact flag. Returns (magnitude, inexact).
30fn float_to_uint128<F: FloatFormat>(
31    a: F,
32    env: &mut FloatEnv,
33) -> (FloatParts, u128, bool) {
34    let parts = unpack::<F>(a);
35    if !matches!(parts.cls, FloatClass::Normal) {
36        return (parts, 0, false);
37    }
38
39    let shift = INT_BIT as i32 - parts.exp;
40    let rm = env.round_mode();
41
42    let (int_val, inexact) = if shift >= 128 {
43        (0u128, parts.frac != 0)
44    } else if shift > 0 {
45        let shift = shift as u32;
46        let remainder = parts.frac & ((1u128 << shift) - 1);
47        let truncated = parts.frac >> shift;
48        let inexact = remainder != 0;
49        let half = 1u128 << (shift - 1);
50        let up =
51            should_round_up_int(remainder, half, truncated, rm, parts.sign);
52        (if up { truncated + 1 } else { truncated }, inexact)
53    } else if shift == 0 {
54        (parts.frac, false)
55    } else {
56        let lshift = (-shift) as u32;
57        if lshift >= 128 {
58            return (parts, u128::MAX, false);
59        }
60        (parts.frac << lshift, false)
61    };
62
63    (parts, int_val, inexact)
64}
65
66// ---------------------------------------------------------------
67// Float-to-integer conversions
68// ---------------------------------------------------------------
69
70pub fn to_i32<F: FloatFormat>(a: F, env: &mut FloatEnv) -> i32 {
71    let parts = unpack::<F>(a);
72
73    if parts.is_nan() {
74        env.raise(ExcFlags::INVALID);
75        return i32::MAX;
76    }
77    if parts.cls == FloatClass::Inf {
78        env.raise(ExcFlags::INVALID);
79        return if parts.sign { i32::MIN } else { i32::MAX };
80    }
81    if parts.cls == FloatClass::Zero {
82        return 0;
83    }
84
85    let (_, mag, inexact) = float_to_uint128::<F>(a, env);
86
87    if parts.sign {
88        if mag > 0x8000_0000 {
89            env.raise(ExcFlags::INVALID);
90            return i32::MIN;
91        }
92        if inexact {
93            env.raise(ExcFlags::INEXACT);
94        }
95        -(mag as i32)
96    } else {
97        if mag > 0x7FFF_FFFF {
98            env.raise(ExcFlags::INVALID);
99            return i32::MAX;
100        }
101        if inexact {
102            env.raise(ExcFlags::INEXACT);
103        }
104        mag as i32
105    }
106}
107
108pub fn to_u32<F: FloatFormat>(a: F, env: &mut FloatEnv) -> u32 {
109    let parts = unpack::<F>(a);
110
111    if parts.is_nan() {
112        env.raise(ExcFlags::INVALID);
113        return u32::MAX;
114    }
115    if parts.cls == FloatClass::Inf {
116        env.raise(ExcFlags::INVALID);
117        return if parts.sign { 0 } else { u32::MAX };
118    }
119    if parts.cls == FloatClass::Zero {
120        return 0;
121    }
122
123    if parts.sign {
124        // Negative: truncate toward zero. If the
125        // magnitude truncates to 0, result is 0 with
126        // INEXACT (value was not zero but rounds to 0).
127        // If magnitude > 0, the negative integer is not
128        // representable as unsigned → INVALID.
129        let (_, mag, inexact) = float_to_uint128::<F>(a, env);
130        if mag > 0 {
131            env.raise(ExcFlags::INVALID);
132        } else if inexact {
133            env.raise(ExcFlags::INEXACT);
134        }
135        return 0;
136    }
137
138    let (_, mag, inexact) = float_to_uint128::<F>(a, env);
139    if mag > u32::MAX as u128 {
140        env.raise(ExcFlags::INVALID);
141        return u32::MAX;
142    }
143    if inexact {
144        env.raise(ExcFlags::INEXACT);
145    }
146    mag as u32
147}
148
149pub fn to_i64<F: FloatFormat>(a: F, env: &mut FloatEnv) -> i64 {
150    let parts = unpack::<F>(a);
151
152    if parts.is_nan() {
153        env.raise(ExcFlags::INVALID);
154        return i64::MAX;
155    }
156    if parts.cls == FloatClass::Inf {
157        env.raise(ExcFlags::INVALID);
158        return if parts.sign { i64::MIN } else { i64::MAX };
159    }
160    if parts.cls == FloatClass::Zero {
161        return 0;
162    }
163
164    let (_, mag, inexact) = float_to_uint128::<F>(a, env);
165
166    if parts.sign {
167        if mag > 0x8000_0000_0000_0000 {
168            env.raise(ExcFlags::INVALID);
169            return i64::MIN;
170        }
171        if inexact {
172            env.raise(ExcFlags::INEXACT);
173        }
174        -(mag as i64)
175    } else {
176        if mag > 0x7FFF_FFFF_FFFF_FFFF {
177            env.raise(ExcFlags::INVALID);
178            return i64::MAX;
179        }
180        if inexact {
181            env.raise(ExcFlags::INEXACT);
182        }
183        mag as i64
184    }
185}
186
187pub fn to_u64<F: FloatFormat>(a: F, env: &mut FloatEnv) -> u64 {
188    let parts = unpack::<F>(a);
189
190    if parts.is_nan() {
191        env.raise(ExcFlags::INVALID);
192        return u64::MAX;
193    }
194    if parts.cls == FloatClass::Inf {
195        env.raise(ExcFlags::INVALID);
196        return if parts.sign { 0 } else { u64::MAX };
197    }
198    if parts.cls == FloatClass::Zero {
199        return 0;
200    }
201
202    if parts.sign {
203        let (_, mag, inexact) = float_to_uint128::<F>(a, env);
204        if mag > 0 {
205            env.raise(ExcFlags::INVALID);
206        } else if inexact {
207            env.raise(ExcFlags::INEXACT);
208        }
209        return 0;
210    }
211
212    let (_, mag, inexact) = float_to_uint128::<F>(a, env);
213    if mag > u64::MAX as u128 {
214        env.raise(ExcFlags::INVALID);
215        return u64::MAX;
216    }
217    if inexact {
218        env.raise(ExcFlags::INEXACT);
219    }
220    mag as u64
221}
222
223fn should_round_up_int(
224    remainder: u128,
225    half: u128,
226    truncated: u128,
227    rm: RoundMode,
228    sign: bool,
229) -> bool {
230    match rm {
231        RoundMode::NearEven => {
232            if remainder > half {
233                true
234            } else if remainder == half {
235                truncated & 1 != 0
236            } else {
237                false
238            }
239        }
240        RoundMode::NearMaxMag => remainder >= half,
241        RoundMode::ToZero => false,
242        RoundMode::Down => sign && remainder != 0,
243        RoundMode::Up => !sign && remainder != 0,
244        RoundMode::Odd => {
245            if remainder != 0 {
246                truncated & 1 == 0
247            } else {
248                false
249            }
250        }
251    }
252}
253
254// ---------------------------------------------------------------
255// Integer-to-float conversions
256// ---------------------------------------------------------------
257
258pub fn from_i32<F: FloatFormat>(a: i32, env: &mut FloatEnv) -> F {
259    if a == 0 {
260        return F::from_bits(<F::Bits as BitOps>::from_u128(0));
261    }
262    let sign = a < 0;
263    let mag = if sign {
264        (a as i64).unsigned_abs()
265    } else {
266        a as u64
267    };
268    from_uint_impl::<F>(mag as u128, sign, env)
269}
270
271pub fn from_u32<F: FloatFormat>(a: u32, env: &mut FloatEnv) -> F {
272    if a == 0 {
273        return F::from_bits(<F::Bits as BitOps>::from_u128(0));
274    }
275    from_uint_impl::<F>(a as u128, false, env)
276}
277
278pub fn from_i64<F: FloatFormat>(a: i64, env: &mut FloatEnv) -> F {
279    if a == 0 {
280        return F::from_bits(<F::Bits as BitOps>::from_u128(0));
281    }
282    let sign = a < 0;
283    let mag = a.unsigned_abs();
284    from_uint_impl::<F>(mag as u128, sign, env)
285}
286
287pub fn from_u64<F: FloatFormat>(a: u64, env: &mut FloatEnv) -> F {
288    if a == 0 {
289        return F::from_bits(<F::Bits as BitOps>::from_u128(0));
290    }
291    from_uint_impl::<F>(a as u128, false, env)
292}
293
294fn from_uint_impl<F: FloatFormat>(
295    mag: u128,
296    sign: bool,
297    env: &mut FloatEnv,
298) -> F {
299    if mag == 0 {
300        return F::from_bits(<F::Bits as BitOps>::from_u128(0));
301    }
302
303    let msb = 127 - mag.leading_zeros();
304    let exp = msb as i32;
305
306    let frac = if msb <= INT_BIT {
307        mag << (INT_BIT - msb)
308    } else {
309        let shift = msb - INT_BIT;
310        let sticky = if mag & ((1u128 << shift) - 1) != 0 {
311            1u128
312        } else {
313            0
314        };
315        (mag >> shift) | sticky
316    };
317
318    let mut parts = FloatParts {
319        sign,
320        exp,
321        frac,
322        cls: FloatClass::Normal,
323    };
324    round_pack::<F>(&mut parts, env)
325}
326
327// ---------------------------------------------------------------
328// Convenience methods for float-to-float conversion
329// ---------------------------------------------------------------
330
331macro_rules! impl_convert_from {
332    ($dst:ty, $src:ty, $method:ident) => {
333        impl $dst {
334            pub fn $method(v: $src, env: &mut FloatEnv) -> Self {
335                convert::<$src, $dst>(v, env)
336            }
337        }
338    };
339}
340
341impl_convert_from!(Float32, Float16, from_f16);
342impl_convert_from!(Float32, BFloat16, from_bf16);
343impl_convert_from!(Float32, Float64, from_f64);
344impl_convert_from!(Float32, Float128, from_f128);
345impl_convert_from!(Float32, FloatX80, from_fx80);
346
347impl_convert_from!(Float64, Float16, from_f16);
348impl_convert_from!(Float64, BFloat16, from_bf16);
349impl_convert_from!(Float64, Float32, from_f32);
350impl_convert_from!(Float64, Float128, from_f128);
351impl_convert_from!(Float64, FloatX80, from_fx80);
352
353impl_convert_from!(Float16, Float32, from_f32);
354impl_convert_from!(Float16, Float64, from_f64);
355impl_convert_from!(Float16, BFloat16, from_bf16);
356impl_convert_from!(Float16, Float128, from_f128);
357impl_convert_from!(Float16, FloatX80, from_fx80);
358
359impl_convert_from!(BFloat16, Float16, from_f16);
360impl_convert_from!(BFloat16, Float32, from_f32);
361impl_convert_from!(BFloat16, Float64, from_f64);
362impl_convert_from!(BFloat16, Float128, from_f128);
363impl_convert_from!(BFloat16, FloatX80, from_fx80);
364
365impl_convert_from!(Float128, Float16, from_f16);
366impl_convert_from!(Float128, BFloat16, from_bf16);
367impl_convert_from!(Float128, Float32, from_f32);
368impl_convert_from!(Float128, Float64, from_f64);
369impl_convert_from!(Float128, FloatX80, from_fx80);
370
371impl_convert_from!(FloatX80, Float16, from_f16);
372impl_convert_from!(FloatX80, BFloat16, from_bf16);
373impl_convert_from!(FloatX80, Float32, from_f32);
374impl_convert_from!(FloatX80, Float64, from_f64);
375impl_convert_from!(FloatX80, Float128, from_f128);
376
377macro_rules! impl_to_int {
378    ($ty:ty) => {
379        impl $ty {
380            pub fn to_i32(self, env: &mut FloatEnv) -> i32 {
381                to_i32::<Self>(self, env)
382            }
383            pub fn to_u32(self, env: &mut FloatEnv) -> u32 {
384                to_u32::<Self>(self, env)
385            }
386            pub fn to_i64(self, env: &mut FloatEnv) -> i64 {
387                to_i64::<Self>(self, env)
388            }
389            pub fn to_u64(self, env: &mut FloatEnv) -> u64 {
390                to_u64::<Self>(self, env)
391            }
392        }
393    };
394}
395
396impl_to_int!(Float16);
397impl_to_int!(BFloat16);
398impl_to_int!(Float32);
399impl_to_int!(Float64);
400impl_to_int!(Float128);
401impl_to_int!(FloatX80);
402
403macro_rules! impl_from_int {
404    ($ty:ty) => {
405        impl $ty {
406            pub fn from_i32(v: i32, env: &mut FloatEnv) -> Self {
407                from_i32::<Self>(v, env)
408            }
409            pub fn from_u32(v: u32, env: &mut FloatEnv) -> Self {
410                from_u32::<Self>(v, env)
411            }
412            pub fn from_i64(v: i64, env: &mut FloatEnv) -> Self {
413                from_i64::<Self>(v, env)
414            }
415            pub fn from_u64(v: u64, env: &mut FloatEnv) -> Self {
416                from_u64::<Self>(v, env)
417            }
418        }
419    };
420}
421
422impl_from_int!(Float16);
423impl_from_int!(BFloat16);
424impl_from_int!(Float32);
425impl_from_int!(Float64);
426impl_from_int!(Float128);
427impl_from_int!(FloatX80);