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}