malachite_float/float/constants/dottie_number.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::float::arithmetic::cos::round_bracket;
11use core::cmp::{Ordering, min};
12use malachite_base::num::arithmetic::traits::{Abs, PowerOf2};
13use malachite_base::num::basic::integers::PrimitiveInt;
14use malachite_base::num::basic::traits::{DottieNumber, One};
15use malachite_base::num::conversion::traits::ExactFrom;
16use malachite_base::rounding_modes::RoundingMode::{self, *};
17use malachite_nz::platform::Limb;
18use malachite_q::Rational;
19
20impl Float {
21 /// Returns an approximation of the Dottie number, the unique real fixed point of the cosine,
22 /// with the given precision and rounded using the given [`RoundingMode`]. An [`Ordering`] is
23 /// also returned, indicating whether the rounded value is less than or greater than the exact
24 /// value of the constant. (Since the constant is irrational, the rounded value is never equal
25 /// to the exact value.)
26 ///
27 /// $$
28 /// x = d+\varepsilon, \quad \text{where } \cos d = d.
29 /// $$
30 /// - If $m$ is not `Nearest`, then $|\varepsilon| < 2^{-p}$.
31 /// - If $m$ is `Nearest`, then $|\varepsilon| < 2^{-p-1}$.
32 ///
33 /// The constant is irrational and transcendental (if $d$ were algebraic, $\cos d$ would be
34 /// transcendental by the Lindemann-Weierstrass theorem, and could not equal $d$).
35 ///
36 /// The output has precision `prec`.
37 ///
38 /// The root of $x - \cos x$ is found by Newton's method with the working precision doubled at
39 /// each step, and the final iterate is certified by bounding the residual $x - \cos x$ with a
40 /// correctly rounded cosine, so that the result is correctly rounded rather than merely the
41 /// fixed point of a rounded cosine.
42 ///
43 /// # Worst-case complexity
44 /// $T(n) = O(n (\log n)^3 \log\log n)$
45 ///
46 /// $M(n) = O(n \log n)$
47 ///
48 /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`.
49 ///
50 /// # Panics
51 /// Panics if `prec` is zero or if `rm` is `Exact`.
52 ///
53 /// # Examples
54 /// ```
55 /// use malachite_base::rounding_modes::RoundingMode::*;
56 /// use malachite_float::Float;
57 /// use std::cmp::Ordering::*;
58 ///
59 /// let (dottie_number, o) = Float::dottie_number_prec_round(100, Floor);
60 /// assert_eq!(
61 /// dottie_number.to_string(),
62 /// "0.73908513321516064165531208767346"
63 /// );
64 /// assert_eq!(o, Less);
65 ///
66 /// let (dottie_number, o) = Float::dottie_number_prec_round(100, Ceiling);
67 /// assert_eq!(
68 /// dottie_number.to_string(),
69 /// "0.73908513321516064165531208767425"
70 /// );
71 /// assert_eq!(o, Greater);
72 /// ```
73 pub fn dottie_number_prec_round(prec: u64, rm: RoundingMode) -> (Self, Ordering) {
74 assert_ne!(prec, 0);
75 assert_ne!(rm, Exact, "Inexact Dottie number");
76 let mut w = prec + 10;
77 let mut increment = Limb::WIDTH;
78 // Newton's method on f(x) = x - cos x, whose derivative 1 + sin x is about 1.67 at the
79 // root, from a double-precision seed; convergence is quadratic, so each step runs at twice
80 // the number of bits the previous iterate got right, and the whole iteration costs little
81 // more than its last step.
82 let mut x = Self::from(f64::DOTTIE_NUMBER);
83 let mut correct = 50;
84 loop {
85 while correct + 2 < w {
86 let p = min(correct << 1, w);
87 let (s, c, _, _) = x.sin_cos_prec_ref(p);
88 let t = x.sub_prec_ref_val(c, p).0;
89 let u = s.add_prec(Self::ONE, p).0;
90 x.sub_prec_assign(t.div_round(u, Nearest).0, p);
91 // the step's error is dominated by the rounding of its cosine
92 correct = p - 2;
93 }
94 // Certification: c = cos x rounded to nearest is within 2^(-w-1) of cos x (c < 1, so
95 // its ulp is 2^-w), so the residual x - cos x is within that of x - c, computed
96 // exactly; and by the mean value theorem |d - x| <= |x - cos x| / min(1 + sin) over [x,
97 // d], where 1 + sin >= 1.6 on [0.7, 0.8] (sin 0.7 > 0.64), so the bracket [x - e, x +
98 // e] with e = (|x - c| + 2^(-w-1)) * 5/8 contains d.
99 assert!(x > 0.7f64 && x < 0.8f64);
100 let c = x.cos_prec_ref(w).0;
101 let xr = Rational::exact_from(&x);
102 let e = ((&xr - Rational::exact_from(&c)).abs()
103 + Rational::power_of_2(-i64::exact_from(w) - 1))
104 * const { Rational::const_from_unsigneds(5, 8) };
105 if let Some(result) = round_bracket(&(&xr - &e), &(xr + e), prec, rm) {
106 return result;
107 }
108 w += increment;
109 increment = w >> 1;
110 }
111 }
112
113 /// Returns an approximation of the Dottie number, the unique real fixed point of the cosine,
114 /// with the given precision and rounded to the nearest [`Float`] of that precision. An
115 /// [`Ordering`] is also returned, indicating whether the rounded value is less than or greater
116 /// than the exact value of the constant. (Since the constant is irrational, the rounded value
117 /// is never equal to the exact value.)
118 ///
119 /// $$
120 /// x = d+\varepsilon, \quad \text{where } \cos d = d.
121 /// $$
122 /// - $|\varepsilon| < 2^{-p-1}$.
123 ///
124 /// The constant is irrational and transcendental.
125 ///
126 /// The output has precision `prec`.
127 ///
128 /// # Worst-case complexity
129 /// $T(n) = O(n (\log n)^3 \log\log n)$
130 ///
131 /// $M(n) = O(n \log n)$
132 ///
133 /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`.
134 ///
135 /// # Panics
136 /// Panics if `prec` is zero.
137 ///
138 /// # Examples
139 /// ```
140 /// use malachite_float::Float;
141 /// use std::cmp::Ordering::*;
142 ///
143 /// let (dottie_number, o) = Float::dottie_number_prec(1);
144 /// assert_eq!(dottie_number.to_string(), "0.50");
145 /// assert_eq!(o, Less);
146 ///
147 /// let (dottie_number, o) = Float::dottie_number_prec(10);
148 /// assert_eq!(dottie_number.to_string(), "0.73926");
149 /// assert_eq!(o, Greater);
150 ///
151 /// let (dottie_number, o) = Float::dottie_number_prec(100);
152 /// assert_eq!(
153 /// dottie_number.to_string(),
154 /// "0.73908513321516064165531208767425"
155 /// );
156 /// assert_eq!(o, Greater);
157 /// ```
158 #[inline]
159 pub fn dottie_number_prec(prec: u64) -> (Self, Ordering) {
160 Self::dottie_number_prec_round(prec, Nearest)
161 }
162}