Skip to main content

malachite_float/float/basic/
subnormalize.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the GNU MPFR Library.
4//
5//      Copyright © 2001-2025 Free Software Foundation, Inc.
6//
7// This file is part of Malachite.
8//
9// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
10// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
11// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
12
13use crate::Float;
14use crate::InnerFloat::Finite;
15use core::cmp::Ordering::{self, *};
16use malachite_base::num::arithmetic::traits::{IsPowerOf2, NegModPowerOf2, PowerOf2};
17use malachite_base::num::basic::integers::PrimitiveInt;
18use malachite_base::num::basic::traits::{NegativeZero, Zero};
19use malachite_base::num::conversion::traits::ExactFrom;
20use malachite_base::num::logic::traits::{BitAccess, LowMask, SignificantBits};
21use malachite_base::rounding_modes::RoundingMode::{self, *};
22use malachite_nz::natural::Natural;
23use malachite_nz::platform::Limb;
24
25// Steps a finite nonzero Float's magnitude down by one ulp of its own precision, with
26// mpfr_nexttozero's behavior at binade boundaries: from a power of 2, the step is into the lower
27// binade, by that binade's smaller ulp. (Float::decrement instead subtracts the departed binade's
28// larger ulp there, and for negative values the direction of Float::increment reverses, so neither
29// is usable directly.)
30fn magnitude_step_toward_zero(x: &mut Float) {
31    let Float(Finite {
32        exponent,
33        precision,
34        significand,
35        ..
36    }) = x
37    else {
38        panic!();
39    };
40    let total = precision.neg_mod_power_of_2(Limb::LOG_WIDTH) + *precision;
41    if significand.is_power_of_2() {
42        *significand = Natural::low_mask(*precision) << (total - *precision);
43        *exponent -= 1;
44    } else {
45        *significand -= Natural::power_of_2(total - *precision);
46    }
47}
48
49// Steps a finite nonzero Float's magnitude up by one ulp of its own precision.
50fn magnitude_step_away_from_zero(x: &mut Float) {
51    let Float(Finite {
52        precision,
53        significand,
54        ..
55    }) = x
56    else {
57        panic!();
58    };
59    let total = precision.neg_mod_power_of_2(Limb::LOG_WIDTH) + *precision;
60    *significand += Natural::power_of_2(total - *precision);
61    // The only caller steps away from a result of rounding to even, whose significand's lowest kept
62    // bit is 0, so the addition cannot carry out of the significand.
63    debug_assert!(significand.significant_bits() <= total);
64}
65
66// The minimum positive value of the emulated format, +/- 2^(sub_exp_min - 1), at precision `prec`.
67fn min_subnormal(sign: bool, sub_exp_min: i64, prec: u64) -> Float {
68    Float(Finite {
69        sign,
70        exponent: i32::exact_from(sub_exp_min),
71        precision: prec,
72        significand: Natural::power_of_2(prec.neg_mod_power_of_2(Limb::LOG_WIDTH) + prec - 1),
73    })
74}
75
76impl Float {
77    // This is a translation of mpfr_subnormalize from subnormal.c, MPFR 4.2.2, with two
78    // differences. First, since Malachite has no global exponent range, the minimum normal exponent
79    // of the emulated format is an explicit argument, as in rug's subnormalize_round; values with
80    // exponents in [normal_exp_min - prec + 1, normal_exp_min) are subnormal in the emulated
81    // format. Second, since a preceding Malachite computation runs in the full exponent range
82    // rather than underflowing at the format's minimum, values below the smallest subnormal are
83    // also handled here (mirroring rug), instead of being clamped by the preceding operation.
84    /// Emulates gradual underflow, adjusting a rounded result as if it had been computed in a
85    /// floating-point format with a limited exponent range and subnormal numbers, such as an IEEE
86    /// 754 format.
87    ///
88    /// `self` should be the result of a computation correctly rounded to its own precision, with
89    /// `o` indicating whether that result is less than, equal to, or greater than the exact value,
90    /// and `rm` the rounding mode that was used. If the value is at least
91    /// $2^{\\text{{normal\\_exp\\_min}}-1}$ in absolute value (or is `NaN`, infinite, or zero), it
92    /// is returned unchanged along with `o`. Otherwise it lies in the emulated format's subnormal
93    /// range, where fewer than `prec` significand bits are available, and it is rounded again to
94    /// the available precision, with a correction that makes the result identical to what a single
95    /// rounding of the exact value into the subnormal format would have produced. The returned
96    /// [`Ordering`] compares the final result to the exact value. Values smaller than half the
97    /// minimum subnormal round to zero.
98    ///
99    /// The precision of the result equals the precision of the input, except that zero results
100    /// carry no precision.
101    ///
102    /// To emulate a standard format, pass the format's minimum normal exponent: for example, $-125$
103    /// for IEEE 754 binary32 and $-1021$ for binary64, using the convention in which the
104    /// significand lies in $[1/2, 1)$. This function is the analogue of `mpfr_subnormalize`, with
105    /// the exponent range passed explicitly rather than set globally, and with values below the
106    /// smallest subnormal handled here rather than by the preceding operation's underflow.
107    ///
108    /// The [`Float`] is modified in place.
109    ///
110    /// # Worst-case complexity
111    /// $T(n) = O(n)$
112    ///
113    /// $M(n) = O(n)$
114    ///
115    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
116    ///
117    /// # Panics
118    /// Panics if `rm` is `Exact` and the value is not exactly representable in the emulated format.
119    ///
120    /// # Examples
121    /// ```
122    /// use core::cmp::Ordering::*;
123    /// use malachite_base::rounding_modes::RoundingMode::*;
124    /// use malachite_float::Float;
125    /// use malachite_nz::natural::Natural;
126    ///
127    /// // In a format with 4 significand bits and minimum normal exponent -5, the value 13 * 2^-10
128    /// // is subnormal, with only 3 significand bits available; rounding ties to even.
129    /// let mut x = Float::from_natural_prec(Natural::from(13u32), 4).0 >> 10u32;
130    /// assert_eq!(x.to_string(), "0.0127");
131    /// let o = x.subnormalize_assign(Equal, -5, Nearest);
132    /// assert_eq!(x.to_string(), "0.0117");
133    /// assert_eq!(x.get_prec(), Some(4));
134    /// assert_eq!(o, Less);
135    /// ```
136    pub fn subnormalize_assign(
137        &mut self,
138        o: Ordering,
139        normal_exp_min: i64,
140        rm: RoundingMode,
141    ) -> Ordering {
142        let Self(Finite {
143            sign,
144            exponent,
145            precision,
146            significand,
147        }) = &*self
148        else {
149            return o;
150        };
151        let sign = *sign;
152        let prec = *precision;
153        let exp = i64::from(*exponent);
154        let sub_exp_min = normal_exp_min - i64::exact_from(prec) + 1;
155        if exp >= normal_exp_min {
156            return o;
157        }
158        if exp < sub_exp_min {
159            // Below the smallest subnormal 2^(sub_exp_min - 1). The rounding tie is at
160            // 2^(sub_exp_min - 2), which the value equals exactly if and only if its exponent is
161            // sub_exp_min - 1 and it is a power of 2; in that case the direction of the exact
162            // result is recovered from the ternary value.
163            let away = match rm {
164                Floor => !sign,
165                Ceiling => sign,
166                Down => false,
167                Up => true,
168                Nearest => {
169                    if exp == sub_exp_min - 1 {
170                        if significand.is_power_of_2() {
171                            // exactly at the tie; round away only if the exact value is beyond the
172                            // approximation
173                            if sign { o == Less } else { o == Greater }
174                        } else {
175                            true
176                        }
177                    } else {
178                        false
179                    }
180                }
181                Exact => panic!(
182                    "subnormalize with Exact: value is below the smallest subnormal of the \
183                     emulated format"
184                ),
185            };
186            return if away {
187                *self = min_subnormal(sign, sub_exp_min, prec);
188                if sign { Greater } else { Less }
189            } else {
190                *self = if sign {
191                    Self::ZERO
192                } else {
193                    Self::NEGATIVE_ZERO
194                };
195                if sign { Less } else { Greater }
196            };
197        }
198        // The value is in the subnormal range, with q available bits.
199        let q = u64::exact_from(exp - sub_exp_min + 1);
200        let min_prec = self.get_min_prec().unwrap();
201        if min_prec <= q {
202            // exactly representable in the emulated format; no second rounding occurs
203            return o;
204        }
205        assert!(
206            rm != Exact,
207            "subnormalize with Exact: value is not exactly representable in the emulated format"
208        );
209        if q == 1 {
210            // Only one bit is available. The rounding bit is the second-highest bit of the
211            // significand and the sticky bit is the disjunction of the rest; this mirrors
212            // mpfr_subnormalize's table for rounding to nearest, in which ties round to the even
213            // multiple of the smallest subnormal, that is, upward.
214            let away = match rm {
215                Floor => !sign,
216                Ceiling => sign,
217                Down => false,
218                Up => true,
219                Nearest => {
220                    let sig_bits = prec.neg_mod_power_of_2(Limb::LOG_WIDTH) + prec;
221                    if !significand.get_bit(sig_bits - 2) {
222                        false
223                    } else if min_prec > 2 {
224                        // the sticky bit is set
225                        true
226                    } else {
227                        // rounding bit 1, sticky bit 0: the value is exactly the tie; round away
228                        // unless the exact result is toward zero from here
229                        if sign { o != Greater } else { o != Less }
230                    }
231                }
232                Exact => unreachable!(),
233            };
234            return if away {
235                *self = min_subnormal(sign, sub_exp_min + 1, prec);
236                if sign { Greater } else { Less }
237            } else {
238                *self = min_subnormal(sign, sub_exp_min, prec);
239                if sign { Less } else { Greater }
240            };
241        }
242        // The general case: round again to q bits and correct for double rounding.
243        let (mut rounded, mut o2) = Self::from_float_prec_round_ref(self, q, rm);
244        // Since values exactly representable at q bits returned early, the second rounding is
245        // always inexact here, so (unlike in mpfr_subnormalize) there is no exact case whose
246        // ternary needs to be replaced by the first rounding's. The correction applies when the
247        // second rounding hit an exact midpoint and applied the even rule, in the same direction
248        // that the first rounding had already taken: the result has then drifted a full ulp from
249        // the exact value, so step back and reverse the reported direction.
250        if o != Equal && rm == Nearest && min_prec == q + 1 && o2 == o {
251            if (o2 == Greater) == sign {
252                // the result was rounded away from zero; step toward zero
253                magnitude_step_toward_zero(&mut rounded);
254            } else {
255                magnitude_step_away_from_zero(&mut rounded);
256            }
257            o2 = o2.reverse();
258        }
259        *self = Self::from_float_prec(rounded, prec).0;
260        o2
261    }
262
263    /// Emulates gradual underflow, adjusting a rounded result as if it had been computed in a
264    /// floating-point format with a limited exponent range and subnormal numbers, such as an IEEE
265    /// 754 format.
266    ///
267    /// `self` should be the result of a computation correctly rounded to its own precision, with
268    /// `o` indicating whether that result is less than, equal to, or greater than the exact value,
269    /// and `rm` the rounding mode that was used. If the value is at least
270    /// $2^{\\text{{normal\\_exp\\_min}}-1}$ in absolute value (or is `NaN`, infinite, or zero), it
271    /// is returned unchanged along with `o`. Otherwise it lies in the emulated format's subnormal
272    /// range, where fewer than `prec` significand bits are available, and it is rounded again to
273    /// the available precision, with a correction that makes the result identical to what a single
274    /// rounding of the exact value into the subnormal format would have produced. The returned
275    /// [`Ordering`] compares the final result to the exact value. Values smaller than half the
276    /// minimum subnormal round to zero.
277    ///
278    /// The precision of the result equals the precision of the input, except that zero results
279    /// carry no precision.
280    ///
281    /// To emulate a standard format, pass the format's minimum normal exponent: for example, $-125$
282    /// for IEEE 754 binary32 and $-1021$ for binary64, using the convention in which the
283    /// significand lies in $[1/2, 1)$. This function is the analogue of `mpfr_subnormalize`, with
284    /// the exponent range passed explicitly rather than set globally, and with values below the
285    /// smallest subnormal handled here rather than by the preceding operation's underflow.
286    ///
287    /// The [`Float`] is taken by value.
288    ///
289    /// # Worst-case complexity
290    /// $T(n) = O(n)$
291    ///
292    /// $M(n) = O(n)$
293    ///
294    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
295    ///
296    /// # Panics
297    /// Panics if `rm` is `Exact` and the value is not exactly representable in the emulated format.
298    ///
299    /// # Examples
300    /// ```
301    /// use core::cmp::Ordering::*;
302    /// use malachite_base::num::arithmetic::traits::PowerOf2;
303    /// use malachite_base::num::basic::traits::One;
304    /// use malachite_base::num::conversion::traits::ExactFrom;
305    /// use malachite_base::num::float::NiceFloat;
306    /// use malachite_base::rounding_modes::RoundingMode::*;
307    /// use malachite_float::Float;
308    /// use malachite_nz::natural::Natural;
309    ///
310    /// // A value at least 2^(normal_exp_min - 1) in absolute value is unchanged.
311    /// let x = Float::from_natural_prec(Natural::from(8u32), 4).0 >> 4u32;
312    /// let (y, o) = x.subnormalize(Equal, -5, Nearest);
313    /// assert_eq!(y.to_string(), "0.500");
314    /// assert_eq!(o, Equal);
315    ///
316    /// // Emulating IEEE 754 binary64: (2^52 + 1) * 2^-1125 rounds to the second-smallest
317    /// // subnormal double.
318    /// let x =
319    ///     Float::from_natural_prec(Natural::power_of_2(52u64) + Natural::ONE, 53).0 >> 1125u32;
320    /// let (y, o) = x.subnormalize(Equal, -1021, Nearest);
321    /// assert_eq!(y.to_string(), "9.8813129168249309e-324");
322    /// assert_eq!(NiceFloat(f64::exact_from(&y)), NiceFloat(1.0e-323));
323    /// assert_eq!(o, Less);
324    /// ```
325    #[inline]
326    pub fn subnormalize(
327        mut self,
328        o: Ordering,
329        normal_exp_min: i64,
330        rm: RoundingMode,
331    ) -> (Self, Ordering) {
332        let o2 = self.subnormalize_assign(o, normal_exp_min, rm);
333        (self, o2)
334    }
335
336    /// Emulates gradual underflow, adjusting a rounded result as if it had been computed in a
337    /// floating-point format with a limited exponent range and subnormal numbers, such as an IEEE
338    /// 754 format.
339    ///
340    /// `self` should be the result of a computation correctly rounded to its own precision, with
341    /// `o` indicating whether that result is less than, equal to, or greater than the exact value,
342    /// and `rm` the rounding mode that was used. If the value is at least
343    /// $2^{\\text{{normal\\_exp\\_min}}-1}$ in absolute value (or is `NaN`, infinite, or zero), it
344    /// is returned unchanged along with `o`. Otherwise it lies in the emulated format's subnormal
345    /// range, where fewer than `prec` significand bits are available, and it is rounded again to
346    /// the available precision, with a correction that makes the result identical to what a single
347    /// rounding of the exact value into the subnormal format would have produced. The returned
348    /// [`Ordering`] compares the final result to the exact value. Values smaller than half the
349    /// minimum subnormal round to zero.
350    ///
351    /// The precision of the result equals the precision of the input, except that zero results
352    /// carry no precision.
353    ///
354    /// To emulate a standard format, pass the format's minimum normal exponent: for example, $-125$
355    /// for IEEE 754 binary32 and $-1021$ for binary64, using the convention in which the
356    /// significand lies in $[1/2, 1)$. This function is the analogue of `mpfr_subnormalize`, with
357    /// the exponent range passed explicitly rather than set globally, and with values below the
358    /// smallest subnormal handled here rather than by the preceding operation's underflow.
359    ///
360    /// The [`Float`] is taken by reference.
361    ///
362    /// # Worst-case complexity
363    /// $T(n) = O(n)$
364    ///
365    /// $M(n) = O(n)$
366    ///
367    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
368    ///
369    /// # Panics
370    /// Panics if `rm` is `Exact` and the value is not exactly representable in the emulated format.
371    ///
372    /// # Examples
373    /// ```
374    /// use core::cmp::Ordering::*;
375    /// use malachite_base::rounding_modes::RoundingMode::*;
376    /// use malachite_float::Float;
377    /// use malachite_nz::natural::Natural;
378    ///
379    /// // Values smaller than half the minimum subnormal round to zero.
380    /// let x = Float::from_natural_prec(Natural::from(8u32), 4).0 >> 15u32;
381    /// assert_eq!(x.to_string(), "0.000244");
382    /// let (y, o) = x.subnormalize_ref(Equal, -5, Nearest);
383    /// assert_eq!(y.to_string(), "0.0");
384    /// assert_eq!(o, Less);
385    /// ```
386    #[inline]
387    pub fn subnormalize_ref(
388        &self,
389        o: Ordering,
390        normal_exp_min: i64,
391        rm: RoundingMode,
392    ) -> (Self, Ordering) {
393        let mut x = self.clone();
394        let o2 = x.subnormalize_assign(o, normal_exp_min, rm);
395        (x, o2)
396    }
397}