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}