Skip to main content

malachite_float/float/basic/
extended.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::Float;
10use crate::InnerFloat::{Finite, NaN};
11use crate::float::conversion::primitive_float_from_float::FloatFromFloatError;
12use crate::float::conversion::rational_from_float::RationalFromFloatError;
13use core::cmp::Ordering::{self, *};
14use core::cmp::max;
15use core::mem::swap;
16use core::ops::{Add, Mul, Shl, ShlAssign, Shr, ShrAssign};
17use malachite_base::num::arithmetic::traits::{CeilingLogBase2, Parity, Sqrt, SqrtAssign};
18use malachite_base::num::basic::integers::PrimitiveInt;
19use malachite_base::num::basic::traits::Zero;
20use malachite_base::num::conversion::traits::{ExactFrom, SaturatingInto};
21use malachite_base::num::logic::traits::SignificantBits;
22use malachite_base::rounding_modes::RoundingMode::{self, *};
23use malachite_nz::natural::arithmetic::float_extras::float_can_round;
24use malachite_nz::natural::arithmetic::float_sub::exponent_shift_compare;
25use malachite_nz::platform::Limb;
26use malachite_q::Rational;
27
28// The `ExtendedFloat` analogue of `crate::floor_and_ceiling`: given the `(floor, ordering)` pair
29// from a computation rounded toward negative infinity, returns the `(floor, ceiling)` pair of
30// `ExtendedFloat`s bracketing the exact result, obtaining the ceiling by incrementing the floor
31// when the value is inexact.
32pub(crate) fn floor_and_ceiling(
33    (floor, o): (ExtendedFloat, Ordering),
34) -> (ExtendedFloat, ExtendedFloat) {
35    let mut ceiling = floor.clone();
36    if o != Equal {
37        ceiling.increment();
38    }
39    (floor, ceiling)
40}
41
42pub_crate_test_struct! {
43#[derive(Clone)]
44ExtendedFloat {
45    pub(crate) x: Float,
46    pub(crate) exp: i64,
47}}
48
49impl ExtendedFloat {
50    fn is_valid(&self) -> bool {
51        if self.x == 0u32 && self.exp != 0 {
52            return false;
53        }
54        let exp = self.x.get_exponent();
55        exp.is_none() || exp == Some(0)
56    }
57
58    pub(crate) fn from_rational_prec_round(
59        value: Rational,
60        prec: u64,
61        rm: RoundingMode,
62    ) -> (Self, Ordering) {
63        if value == 0 {
64            return (
65                Self {
66                    x: Float::ZERO,
67                    exp: 0,
68                },
69                Equal,
70            );
71        }
72        let exp = value.floor_log_base_2_abs() + 1;
73        let (x, o) = Float::from_rational_prec_round(value >> exp, prec, rm);
74        let new_exp = x.get_exponent().unwrap();
75        (
76            Self {
77                x: x >> new_exp,
78                exp: i64::from(new_exp) + exp,
79            },
80            o,
81        )
82    }
83
84    pub(crate) fn from_rational_prec_round_ref(
85        value: &Rational,
86        prec: u64,
87        rm: RoundingMode,
88    ) -> (Self, Ordering) {
89        if *value == 0 {
90            return (
91                Self {
92                    x: Float::ZERO,
93                    exp: 0,
94                },
95                Equal,
96            );
97        }
98        let exp = value.floor_log_base_2_abs() + 1;
99        if exp >= i64::from(Float::MIN_EXPONENT) && exp <= i64::from(Float::MAX_EXPONENT) {
100            let (x, o) = Float::from_rational_prec_round_ref(value, prec, rm);
101            let exp = x.get_exponent().unwrap();
102            return (
103                Self {
104                    x: x >> exp,
105                    exp: i64::from(exp),
106                },
107                o,
108            );
109        }
110        let (x, o) = Float::from_rational_prec_round(value >> exp, prec, rm);
111        let new_exp = x.get_exponent().unwrap();
112        (
113            Self {
114                x: x >> new_exp,
115                exp: i64::from(new_exp) + exp,
116            },
117            o,
118        )
119    }
120
121    fn add_prec_ref_ref(&self, other: &Self, prec: u64) -> (Self, Ordering) {
122        assert!(self.is_valid());
123        assert!(other.is_valid());
124        assert!(self.x.is_normal());
125        assert!(other.x.is_normal());
126        Self::from_rational_prec_round(
127            Rational::exact_from(self) + Rational::exact_from(other),
128            prec,
129            Nearest,
130        )
131    }
132
133    fn sub_prec_ref_ref(&self, other: &Self, prec: u64) -> (Self, Ordering) {
134        assert!(self.is_valid());
135        assert!(other.is_valid());
136        assert!(self.x.is_normal());
137        assert!(other.x.is_normal());
138        Self::from_rational_prec_round(
139            Rational::exact_from(self) - Rational::exact_from(other),
140            prec,
141            Nearest,
142        )
143    }
144
145    fn sub_prec(self, other: Self, prec: u64) -> (Self, Ordering) {
146        assert!(self.is_valid());
147        assert!(other.is_valid());
148        assert!(self.x.is_normal());
149        assert!(other.x.is_normal());
150        Self::from_rational_prec_round(
151            Rational::exact_from(self) - Rational::exact_from(other),
152            prec,
153            Nearest,
154        )
155    }
156
157    pub(crate) fn mul_prec_ref_ref(&self, other: &Self, prec: u64) -> (Self, Ordering) {
158        assert!(self.is_valid());
159        assert!(other.is_valid());
160        assert!(self.x.is_normal());
161        assert!(other.x.is_normal());
162        let (mut product, o) = self.x.mul_prec_ref_ref(&other.x, prec);
163        let mut product_exp = self.exp + other.exp;
164        let extra_exp = product.get_exponent().unwrap();
165        product >>= extra_exp;
166        product_exp = product_exp.checked_add(i64::from(extra_exp)).unwrap();
167        (
168            Self {
169                x: product,
170                exp: product_exp,
171            },
172            o,
173        )
174    }
175
176    pub(crate) fn div_prec_val_ref(self, other: &Self, prec: u64) -> (Self, Ordering) {
177        assert!(self.is_valid());
178        assert!(other.is_valid());
179        assert!(self.x.is_normal());
180        assert!(other.x.is_normal());
181        let (mut quotient, o) = self.x.div_prec_ref_ref(&other.x, prec);
182        let mut quotient_exp = self.exp - other.exp;
183        let extra_exp = quotient.get_exponent().unwrap();
184        quotient >>= extra_exp;
185        quotient_exp = quotient_exp.checked_add(i64::from(extra_exp)).unwrap();
186        (
187            Self {
188                x: quotient,
189                exp: quotient_exp,
190            },
191            o,
192        )
193    }
194
195    fn div_prec_assign_ref(&mut self, other: &Self, prec: u64) -> Ordering {
196        let mut x = Self {
197            x: Float::ZERO,
198            exp: 0,
199        };
200        swap(self, &mut x);
201        let (q, o) = x.div_prec_val_ref(other, prec);
202        *self = q;
203        o
204    }
205
206    fn square_round_assign(&mut self, rm: RoundingMode) -> Ordering {
207        let mut x = Self {
208            x: Float::ZERO,
209            exp: 0,
210        };
211        swap(self, &mut x);
212        let (mut square, o) = x.x.square_round(rm);
213        let mut square_exp = x.exp << 1;
214        let extra_exp = square.get_exponent().unwrap();
215        square >>= extra_exp;
216        square_exp = square_exp.checked_add(i64::from(extra_exp)).unwrap();
217        *self = Self {
218            x: square,
219            exp: square_exp,
220        };
221        o
222    }
223
224    fn from_extended_float_prec_round(x: Self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
225        if let Ok(x) = Rational::try_from(&x) {
226            Self::from_rational_prec_round(x, prec, rm)
227        } else {
228            (x, Equal)
229        }
230    }
231
232    pub_crate_test! {from_extended_float_prec_round_ref(
233        x: &Self,
234        prec: u64,
235        rm: RoundingMode,
236    ) -> (Self, Ordering) {
237        if let Ok(x) = Rational::try_from(x) {
238            Self::from_rational_prec_round(x, prec, rm)
239        } else {
240            (x.clone(), Equal)
241        }
242    }}
243
244    fn shr_prec_round<T: PrimitiveInt>(
245        self,
246        bits: T,
247        prec: u64,
248        rm: RoundingMode,
249    ) -> (Self, Ordering) {
250        // assumes no overflow or underflow
251        let (out_x, o) = Self::from_extended_float_prec_round(self, prec, rm);
252        let out_exp =
253            i64::exact_from(i128::from(out_x.exp) - SaturatingInto::<i128>::saturating_into(bits));
254        (
255            Self {
256                x: out_x.x,
257                exp: out_exp,
258            },
259            o,
260        )
261    }
262
263    fn shr_round_assign<T: PrimitiveInt>(&mut self, bits: T, _rm: RoundingMode) -> Ordering {
264        // assumes no overflow or underflow
265        if self.x.is_normal() {
266            let out_exp = i64::exact_from(
267                i128::from(self.exp) - SaturatingInto::<i128>::saturating_into(bits),
268            );
269            self.exp = out_exp;
270        }
271        Equal
272    }
273
274    fn shl_round<T: PrimitiveInt>(mut self, bits: T, rm: RoundingMode) -> (Self, Ordering) {
275        let o = self.shl_round_assign(bits, rm);
276        (self, o)
277    }
278
279    fn shl_round_ref<T: PrimitiveInt>(&self, bits: T, _rm: RoundingMode) -> (Self, Ordering) {
280        // assumes no overflow or underflow
281        if self.x.is_normal() {
282            let out_exp = i64::exact_from(
283                i128::from(self.exp) + SaturatingInto::<i128>::saturating_into(bits),
284            );
285            (
286                Self {
287                    x: self.x.clone(),
288                    exp: out_exp,
289                },
290                Equal,
291            )
292        } else {
293            (self.clone(), Equal)
294        }
295    }
296
297    fn shl_round_assign<T: PrimitiveInt>(&mut self, bits: T, _rm: RoundingMode) -> Ordering {
298        // assumes no overflow or underflow
299        if self.x.is_normal() {
300            let out_exp = i64::exact_from(
301                i128::from(self.exp) + SaturatingInto::<i128>::saturating_into(bits),
302            );
303            self.exp = out_exp;
304        }
305        Equal
306    }
307
308    pub(crate) fn into_float_helper(
309        mut self,
310        prec: u64,
311        rm: RoundingMode,
312        previous_o: Ordering,
313    ) -> (Float, Ordering) {
314        let o = self
315            .x
316            .shl_prec_round_assign_helper(self.exp, prec, rm, previous_o);
317        (self.x, o)
318    }
319
320    pub(crate) fn increment(&mut self) {
321        self.x.increment();
322        if let Some(exp) = self.x.get_exponent()
323            && exp == 1
324        {
325            self.x >>= 1u32;
326            self.exp = 0;
327        }
328    }
329}
330
331impl From<Float> for ExtendedFloat {
332    fn from(value: Float) -> Self {
333        if let Some(exp) = value.get_exponent() {
334            Self {
335                x: value >> exp,
336                exp: i64::from(exp),
337            }
338        } else {
339            Self { x: value, exp: 0 }
340        }
341    }
342}
343
344impl TryFrom<ExtendedFloat> for Float {
345    type Error = FloatFromFloatError;
346
347    fn try_from(value: ExtendedFloat) -> Result<Self, Self::Error> {
348        if value.x.is_normal() {
349            let y = value.x << value.exp;
350            if y.is_normal() {
351                Ok(y)
352            } else {
353                Err(if value.exp > 0 {
354                    FloatFromFloatError::Overflow
355                } else {
356                    FloatFromFloatError::Underflow
357                })
358            }
359        } else {
360            Ok(value.x)
361        }
362    }
363}
364
365impl TryFrom<ExtendedFloat> for Rational {
366    type Error = RationalFromFloatError;
367
368    #[inline]
369    fn try_from(value: ExtendedFloat) -> Result<Self, Self::Error> {
370        Self::try_from(value.x).map(|x| x << value.exp)
371    }
372}
373
374impl<'a> TryFrom<&'a ExtendedFloat> for Rational {
375    type Error = RationalFromFloatError;
376
377    #[inline]
378    fn try_from(value: &'a ExtendedFloat) -> Result<Self, Self::Error> {
379        Self::try_from(&value.x).map(|x| x << value.exp)
380    }
381}
382
383impl PartialEq for ExtendedFloat {
384    fn eq(&self, other: &Self) -> bool {
385        self.x == other.x && self.exp == other.exp
386    }
387}
388
389impl PartialOrd for ExtendedFloat {
390    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
391        assert!(self.is_valid());
392        assert!(other.is_valid());
393        let self_sign = self.x > 0u32;
394        let other_sign = other.x > 0u32;
395        match self_sign.cmp(&other_sign) {
396            Greater => Some(Greater),
397            Less => Some(Less),
398            Equal => match self.exp.cmp(&other.exp) {
399                Greater => Some(if self_sign { Greater } else { Less }),
400                Less => Some(if self_sign { Less } else { Greater }),
401                Equal => self.x.partial_cmp(&other.x),
402            },
403        }
404    }
405}
406
407impl Add<&ExtendedFloat> for &ExtendedFloat {
408    type Output = ExtendedFloat;
409
410    fn add(self, other: &ExtendedFloat) -> Self::Output {
411        let prec = max(self.x.significant_bits(), other.x.significant_bits());
412        self.add_prec_ref_ref(other, prec).0
413    }
414}
415
416impl Mul<&ExtendedFloat> for &ExtendedFloat {
417    type Output = ExtendedFloat;
418
419    fn mul(self, other: &ExtendedFloat) -> Self::Output {
420        let prec = max(self.x.significant_bits(), other.x.significant_bits());
421        self.mul_prec_ref_ref(other, prec).0
422    }
423}
424
425impl SqrtAssign for ExtendedFloat {
426    fn sqrt_assign(&mut self) {
427        if self.exp.odd() {
428            self.x <<= 1;
429            self.exp = self.exp.checked_sub(1).unwrap();
430        }
431        self.x.sqrt_assign();
432        self.exp >>= 1;
433        if let Some(new_exp) = self.x.get_exponent() {
434            self.exp = self.exp.checked_add(i64::from(new_exp)).unwrap();
435            self.x >>= new_exp;
436        }
437        assert!(self.is_valid());
438    }
439}
440
441impl Sqrt for ExtendedFloat {
442    type Output = Self;
443
444    fn sqrt(mut self) -> Self::Output {
445        self.sqrt_assign();
446        self
447    }
448}
449
450impl Shr<u32> for ExtendedFloat {
451    type Output = Self;
452
453    fn shr(mut self, bits: u32) -> Self::Output {
454        self.shr_round_assign(bits, Nearest);
455        self
456    }
457}
458
459impl ShrAssign<u32> for ExtendedFloat {
460    fn shr_assign(&mut self, bits: u32) {
461        self.shr_round_assign(bits, Nearest);
462    }
463}
464
465impl ShlAssign<u32> for ExtendedFloat {
466    fn shl_assign(&mut self, bits: u32) {
467        self.shl_round_assign(bits, Nearest);
468    }
469}
470
471impl<T: PrimitiveInt> Shl<T> for &ExtendedFloat {
472    type Output = ExtendedFloat;
473
474    fn shl(self, bits: T) -> ExtendedFloat {
475        self.shl_round_ref(bits, Nearest).0
476    }
477}
478
479impl<T: PrimitiveInt> Shl<T> for ExtendedFloat {
480    type Output = Self;
481
482    fn shl(self, bits: T) -> Self {
483        self.shl_round(bits, Nearest).0
484    }
485}
486
487fn cmp2_helper_extended(b: &ExtendedFloat, c: &ExtendedFloat, cancel: &mut u64) -> Ordering {
488    match (&b.x, &c.x) {
489        (
490            Float(Finite {
491                precision: x_prec,
492                significand: x,
493                ..
494            }),
495            Float(Finite {
496                precision: y_prec,
497                significand: y,
498                ..
499            }),
500        ) => {
501            let (o, c) = exponent_shift_compare(
502                x.as_limbs_asc(),
503                b.exp,
504                *x_prec,
505                y.as_limbs_asc(),
506                c.exp,
507                *y_prec,
508            );
509            *cancel = c;
510            o
511        }
512        _ => panic!(),
513    }
514}
515
516pub(crate) fn agm_prec_round_normal_extended(
517    mut a: ExtendedFloat,
518    mut b: ExtendedFloat,
519    prec: u64,
520    rm: RoundingMode,
521) -> (ExtendedFloat, Ordering) {
522    if a.x < 0u32 || b.x < 0u32 {
523        return (
524            ExtendedFloat {
525                x: float_nan!(),
526                exp: 0,
527            },
528            Equal,
529        );
530    }
531    let q = prec;
532    let mut working_prec = q + q.ceiling_log_base_2() + 15;
533    // b (op2) and a (op1) are the 2 operands but we want b >= a
534    match a.partial_cmp(&b).unwrap() {
535        Equal => return ExtendedFloat::from_extended_float_prec_round(a, prec, rm),
536        Greater => swap(&mut a, &mut b),
537        _ => {}
538    }
539    let mut increment = Limb::WIDTH;
540    let mut v;
541    let mut scaleit;
542    loop {
543        let mut err: u64 = 0;
544        let mut u = a.mul_prec_ref_ref(&b, working_prec).0;
545        v = a.add_prec_ref_ref(&b, working_prec).0;
546        u.sqrt_assign();
547        v >>= 1u32;
548        scaleit = 0;
549        let mut n: u64 = 1;
550        let mut eq = 0;
551        while cmp2_helper_extended(&u, &v, &mut eq) != Equal && eq <= working_prec - 2 {
552            let mut vf;
553            vf = (&u + &v) >> 1;
554            // See proof in algorithms.tex
555            if eq > working_prec >> 2 {
556                // vf = V(k)
557                let low_p = (working_prec + 1) >> 1;
558                let mut w = v.sub_prec_ref_ref(&u, low_p).0; // e = V(k-1)-U(k-1)
559                w.square_round_assign(Nearest); // e = e^2
560                w.shr_round_assign(4, Nearest); // e*= (1/2)^2*1/4
561                w.div_prec_assign_ref(&vf, low_p); // 1/4*e^2/V(k)
562                let vf_exp = vf.exp;
563                v = vf.sub_prec(w, working_prec).0;
564                // 0 or 1
565                err = u64::exact_from(vf_exp - v.exp);
566                break;
567            }
568            let uf = &u * &v;
569            u = uf.sqrt();
570            swap(&mut v, &mut vf);
571            n += 1;
572        }
573        // the error on v is bounded by (18n+51) ulps, or twice if there was an exponent loss in the
574        // final subtraction
575        //
576        // 18n+51 should not overflow since n is about log(p)
577        err += (18 * n + 51).ceiling_log_base_2();
578        // we should have n+2 <= 2^(p/4) [see algorithms.tex]
579        if (n + 2).ceiling_log_base_2() <= working_prec >> 2
580            && float_can_round(v.x.significand_ref().unwrap(), working_prec - err, q, rm)
581        {
582            break;
583        }
584        working_prec += increment;
585        increment = working_prec >> 1;
586    }
587    v.shr_prec_round(scaleit, prec, rm)
588}
589
590pub_crate_test! {agm_prec_round_normal_ref_ref_extended<'a>(
591    mut a: &'a ExtendedFloat,
592    mut b: &'a ExtendedFloat,
593    prec: u64,
594    rm: RoundingMode,
595) -> (ExtendedFloat, Ordering) {
596    if a.x < 0u32 || b.x < 0u32 {
597        return (
598            ExtendedFloat {
599                x: float_nan!(),
600                exp: 0,
601            },
602            Equal,
603        );
604    }
605    let q = prec;
606    let mut working_prec = q + q.ceiling_log_base_2() + 15;
607    // b (op2) and a (op1) are the 2 operands but we want b >= a
608    match a.partial_cmp(b).unwrap() {
609        Equal => return ExtendedFloat::from_extended_float_prec_round_ref(a, prec, rm),
610        Greater => swap(&mut a, &mut b),
611        _ => {}
612    }
613    let mut increment = Limb::WIDTH;
614    let mut v;
615    let mut scaleit;
616    loop {
617        let mut err: u64 = 0;
618        let mut u = a.mul_prec_ref_ref(b, working_prec).0;
619        v = a.add_prec_ref_ref(b, working_prec).0;
620        u.sqrt_assign();
621        v >>= 1u32;
622        scaleit = 0;
623        let mut n: u64 = 1;
624        let mut eq = 0;
625        while cmp2_helper_extended(&u, &v, &mut eq) != Equal && eq <= working_prec - 2 {
626            let mut vf;
627            vf = (&u + &v) >> 1;
628            // See proof in algorithms.tex
629            if eq > working_prec >> 2 {
630                // vf = V(k)
631                let low_p = (working_prec + 1) >> 1;
632                let mut w = v.sub_prec_ref_ref(&u, low_p).0; // e = V(k-1)-U(k-1)
633                assert!(w.is_valid());
634                assert!(w.x.is_normal());
635                w.square_round_assign(Nearest); // e = e^2
636                assert!(w.is_valid());
637                assert!(w.x.is_normal());
638                w.shr_round_assign(4, Nearest); // e*= (1/2)^2*1/4
639                assert!(w.is_valid());
640                assert!(w.x.is_normal());
641                w.div_prec_assign_ref(&vf, low_p); // 1/4*e^2/V(k)
642                let vf_exp = vf.exp;
643                v = vf.sub_prec(w, working_prec).0;
644                // 0 or 1
645                err = u64::exact_from(vf_exp - v.exp);
646                break;
647            }
648            let uf = &u * &v;
649            u = uf.sqrt();
650            swap(&mut v, &mut vf);
651            n += 1;
652        }
653        // the error on v is bounded by (18n+51) ulps, or twice if there was an exponent loss in the
654        // final subtraction
655        //
656        // 18n+51 should not overflow since n is about log(p)
657        err += (18 * n + 51).ceiling_log_base_2();
658        // we should have n+2 <= 2^(p/4) [see algorithms.tex]
659        if (n + 2).ceiling_log_base_2() <= working_prec >> 2
660            && float_can_round(v.x.significand_ref().unwrap(), working_prec - err, q, rm)
661        {
662            break;
663        }
664        working_prec += increment;
665        increment = working_prec >> 1;
666    }
667    v.shr_prec_round(scaleit, prec, rm)
668}}