Skip to main content

malachite_float/float/constants/
pi.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the GNU MPFR Library.
4//
5//      Copyright 1999-2024 Free Software Foundation, Inc.
6//
7//      Contributed by the AriC and Caramba projects, INRIA.
8//
9// This file is part of Malachite.
10//
11// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
12// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
13// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
14
15use crate::Float;
16use core::cmp::Ordering;
17use malachite_base::num::arithmetic::traits::{Sqrt, Square};
18use malachite_base::num::basic::integers::PrimitiveInt;
19use malachite_base::num::conversion::traits::ExactFrom;
20use malachite_base::rounding_modes::RoundingMode::{self, *};
21use malachite_nz::natural::arithmetic::float::round::float_can_round;
22use malachite_nz::platform::Limb;
23#[cfg(feature = "test_build")]
24use std::sync::{Mutex, PoisonError};
25
26// Test builds only: the extreme trig tests each need pi to about 2^30 bits (several minutes per
27// computation), both directly and inside argument reduction. This cache keeps the most accurate
28// unrounded approximation computed so far, along with the number of its bits known to be correct,
29// and answers any later request that the approximation can provably round, using the same
30// `float_can_round` test as a fresh computation. Small precisions bypass it, so pi's own tests and
31// the property tests exercise the real computation.
32#[cfg(feature = "test_build")]
33const PI_CACHE_MIN_PREC: u64 = 1 << 20;
34
35#[cfg(feature = "test_build")]
36static PI_CACHE: Mutex<Option<(Float, u64)>> = Mutex::new(None);
37
38#[cfg(feature = "test_build")]
39fn pi_from_cache(prec: u64, rm: RoundingMode) -> Option<(Float, Ordering)> {
40    let cache = PI_CACHE.lock().unwrap_or_else(PoisonError::into_inner);
41    let (pi, err_prec) = cache.as_ref()?;
42    float_can_round(pi.significand_ref().unwrap(), *err_prec, prec, rm)
43        .then(|| Float::from_float_prec_round_ref(pi, prec, rm))
44}
45
46#[cfg(feature = "test_build")]
47fn pi_to_cache(pi: &Float, err_prec: u64) {
48    let mut cache = PI_CACHE.lock().unwrap_or_else(PoisonError::into_inner);
49    if cache
50        .as_ref()
51        .is_none_or(|&(_, cached_err_prec)| cached_err_prec < err_prec)
52    {
53        *cache = Some((pi.clone(), err_prec));
54    }
55}
56
57impl Float {
58    /// Returns an approximation of $\pi$, with the given precision and rounded using the given
59    /// [`RoundingMode`]. An [`Ordering`] is also returned, indicating whether the rounded value is
60    /// less than or greater than the exact value of the constant. (Since the constant is
61    /// irrational, the rounded value is never equal to the exact value.)
62    ///
63    /// $$
64    /// x = \pi+\varepsilon.
65    /// $$
66    /// - If $m$ is not `Nearest`, then $|\varepsilon| < 2^{-p+2}$.
67    /// - If $m$ is `Nearest`, then $|\varepsilon| < 2^{-p+1}$.
68    ///
69    /// The constant is irrational and transcendental.
70    ///
71    /// The output has precision `prec`.
72    ///
73    /// # Worst-case complexity
74    /// $T(n) = O(n (\log n)^2 \log\log n)$
75    ///
76    /// $M(n) = O(n \log n)$
77    ///
78    /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`: the iteration holds a
79    /// bounded number of working-precision values at a time (freeing each round's temporaries), so
80    /// the peak is the internal memory of a single full-precision multiplication.
81    ///
82    /// # Panics
83    /// Panics if `prec` is zero or if `rm` is `Exact`.
84    ///
85    /// # Examples
86    /// ```
87    /// use malachite_base::rounding_modes::RoundingMode::*;
88    /// use malachite_float::Float;
89    /// use std::cmp::Ordering::*;
90    ///
91    /// let (pi, o) = Float::pi_prec_round(100, Floor);
92    /// assert_eq!(pi.to_string(), "3.1415926535897932384626433832793");
93    /// assert_eq!(o, Less);
94    ///
95    /// let (pi, o) = Float::pi_prec_round(100, Ceiling);
96    /// assert_eq!(pi.to_string(), "3.1415926535897932384626433832825");
97    /// assert_eq!(o, Greater);
98    /// ```
99    ///
100    // This is mpfr_const_pi_internal from const_pi.c, MPFR 4.2.0.
101    #[inline]
102    pub fn pi_prec_round(prec: u64, rm: RoundingMode) -> (Self, Ordering) {
103        #[cfg(feature = "test_build")]
104        if prec >= PI_CACHE_MIN_PREC
105            && let Some(result) = pi_from_cache(prec, rm)
106        {
107            return result;
108        }
109        // we need 9 * 2 ^ kmax - 4 >= px + 2 * kmax + 8
110        let mut kmax = 2;
111        while ((prec + (kmax << 1) + 12) / 9) >> kmax != 0 {
112            kmax += 1;
113        }
114        // guarantees no recomputation for px <= 10000
115        let mut working_prec = prec + 3 * kmax + 14;
116        let mut increment = Limb::WIDTH;
117        loop {
118            let mut a = Self::one_prec(working_prec);
119            let mut big_a = a.clone();
120            let mut big_b = Self::one_half_prec(working_prec);
121            let mut big_d = Self::one_prec(working_prec) >> 2u32;
122            let mut k = 0;
123            loop {
124                let s = (&big_a + &big_b) >> 2u32;
125                a = (a + big_b.sqrt()) >> 1u32;
126                big_a = (&a).square();
127                big_b = (&big_a - s) << 1u32;
128                let mut s = &big_a - &big_b;
129                assert!(s < 1u32);
130                let ip = i64::exact_from(working_prec);
131                let cancel = if s == 0u32 {
132                    ip
133                } else {
134                    i64::from(-s.get_exponent().unwrap())
135                };
136                s <<= k;
137                big_d -= s;
138                // stop when |A_k - B_k| <= 2 ^ (k - p) i.e. cancel >= p - k
139                if cancel >= ip - i64::exact_from(k) {
140                    break;
141                }
142                k += 1;
143            }
144            let pi: Self = big_b / big_d;
145            let err_prec = working_prec - (k << 1) - 8;
146            if float_can_round(pi.significand_ref().unwrap(), err_prec, prec, rm) {
147                #[cfg(feature = "test_build")]
148                if prec >= PI_CACHE_MIN_PREC {
149                    pi_to_cache(&pi, err_prec);
150                }
151                return Self::from_float_prec_round(pi, prec, rm);
152            }
153            working_prec += kmax + increment;
154            increment = working_prec >> 1;
155        }
156    }
157
158    /// Returns an approximation of $\pi$, with the given precision and rounded to the nearest
159    /// [`Float`] of that precision. An [`Ordering`] is also returned, indicating whether the
160    /// rounded value is less than or greater than the exact value of the constant. (Since the
161    /// constant is irrational, the rounded value is never equal to the exact value.)
162    ///
163    /// $$
164    /// x = \pi+\varepsilon.
165    /// $$
166    /// - $|\varepsilon| < 2^{-p+1}$.
167    ///
168    /// The constant is irrational and transcendental.
169    ///
170    /// The output has precision `prec`.
171    ///
172    /// # Worst-case complexity
173    /// $T(n) = O(n (\log n)^2 \log\log n)$
174    ///
175    /// $M(n) = O(n \log n)$
176    ///
177    /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`: the iteration holds a
178    /// bounded number of working-precision values at a time (freeing each round's temporaries), so
179    /// the peak is the internal memory of a single full-precision multiplication.
180    ///
181    /// # Panics
182    /// Panics if `prec` is zero.
183    ///
184    /// # Examples
185    /// ```
186    /// use malachite_float::Float;
187    /// use std::cmp::Ordering::*;
188    ///
189    /// let (pi, o) = Float::pi_prec(1);
190    /// assert_eq!(pi.to_string(), "4.0");
191    /// assert_eq!(o, Greater);
192    ///
193    /// let (pi, o) = Float::pi_prec(10);
194    /// assert_eq!(pi.to_string(), "3.1406");
195    /// assert_eq!(o, Less);
196    ///
197    /// let (pi, o) = Float::pi_prec(100);
198    /// assert_eq!(pi.to_string(), "3.1415926535897932384626433832793");
199    /// assert_eq!(o, Less);
200    /// ```
201    #[inline]
202    pub fn pi_prec(prec: u64) -> (Self, Ordering) {
203        Self::pi_prec_round(prec, Nearest)
204    }
205}