Skip to main content

machina_softfloat/ops/
round.rs

1// SPDX-License-Identifier: MIT
2// IEEE 754 round-to-integer (in floating-point format).
3
4use crate::env::{ExcFlags, FloatEnv, RoundMode};
5use crate::parts::{
6    nan_propagate_one, round_pack, unpack, FloatClass, FloatParts,
7};
8use crate::types::{
9    BFloat16, Float128, Float16, Float32, Float64, FloatFormat, FloatX80,
10};
11
12const INT_BIT: u32 = 126;
13
14/// Round a floating-point number to an integer value,
15/// returning the result in the same float format.
16pub fn round_to_int<F: FloatFormat>(a: F, env: &mut FloatEnv) -> F {
17    let pa = unpack::<F>(a);
18
19    if pa.is_nan() {
20        let mut r = nan_propagate_one(&pa, env);
21        return round_pack::<F>(&mut r, env);
22    }
23
24    if pa.cls == FloatClass::Inf || pa.cls == FloatClass::Zero {
25        let mut r = pa;
26        return round_pack::<F>(&mut r, env);
27    }
28
29    // If exponent is large enough that all fraction bits
30    // are integer bits, the number is already integral.
31    if pa.exp >= F::FRAC_BITS as i32 {
32        let mut r = pa;
33        return round_pack::<F>(&mut r, env);
34    }
35
36    // If exponent is very negative, the magnitude is < 1.
37    if pa.exp < 0 {
38        let rm = env.round_mode();
39        let inexact = pa.frac != 0;
40        if inexact {
41            env.raise(ExcFlags::INEXACT);
42        }
43
44        let round_up = match rm {
45            RoundMode::NearEven => {
46                // Round to even: 0.5 exactly rounds to 0
47                // (even), anything above 0.5 rounds to 1.
48                pa.exp == -1 && pa.frac > (1u128 << INT_BIT)
49            }
50            RoundMode::NearMaxMag => pa.exp == -1,
51            RoundMode::ToZero => false,
52            RoundMode::Up => !pa.sign && inexact,
53            RoundMode::Down => pa.sign && inexact,
54            RoundMode::Odd => true,
55        };
56
57        if round_up {
58            // Return +-1.0
59            let mut r = FloatParts {
60                sign: pa.sign,
61                exp: 0,
62                frac: 1u128 << INT_BIT,
63                cls: FloatClass::Normal,
64            };
65            return round_pack::<F>(&mut r, env);
66        }
67
68        // Return +-0
69        let mut r = FloatParts {
70            sign: pa.sign,
71            exp: 0,
72            frac: 0,
73            cls: FloatClass::Zero,
74        };
75        return round_pack::<F>(&mut r, env);
76    }
77
78    // 0 <= exp < FRAC_BITS: some fractional bits exist.
79    // Mask off the fractional bits and round.
80    let frac_shift = INT_BIT - pa.exp as u32;
81    let frac_mask = (1u128 << frac_shift) - 1;
82    let remainder = pa.frac & frac_mask;
83
84    if remainder == 0 {
85        // Already an integer.
86        let mut r = pa;
87        return round_pack::<F>(&mut r, env);
88    }
89
90    env.raise(ExcFlags::INEXACT);
91
92    let rm = env.round_mode();
93    let half = 1u128 << (frac_shift - 1);
94    let truncated = pa.frac & !frac_mask;
95    let lsb_set = (pa.frac >> frac_shift) & 1 != 0;
96    let increment = 1u128 << frac_shift;
97
98    let round_up = match rm {
99        RoundMode::NearEven => {
100            if remainder > half {
101                true
102            } else if remainder == half {
103                lsb_set
104            } else {
105                false
106            }
107        }
108        RoundMode::NearMaxMag => remainder >= half,
109        RoundMode::ToZero => false,
110        RoundMode::Up => !pa.sign,
111        RoundMode::Down => pa.sign,
112        RoundMode::Odd => {
113            // Set LSB to 1 if inexact.
114            !lsb_set
115        }
116    };
117
118    let frac = if round_up {
119        truncated.wrapping_add(increment)
120    } else {
121        truncated
122    };
123
124    let mut r = FloatParts {
125        sign: pa.sign,
126        exp: pa.exp,
127        frac,
128        cls: FloatClass::Normal,
129    };
130    round_pack::<F>(&mut r, env)
131}
132
133// ---------------------------------------------------------------
134// Convenience methods
135// ---------------------------------------------------------------
136
137macro_rules! impl_round {
138    ($ty:ty) => {
139        impl $ty {
140            pub fn round_to_int(self, env: &mut FloatEnv) -> Self {
141                round_to_int::<Self>(self, env)
142            }
143        }
144    };
145}
146
147impl_round!(Float16);
148impl_round!(BFloat16);
149impl_round!(Float32);
150impl_round!(Float64);
151impl_round!(Float128);
152impl_round!(FloatX80);