Skip to main content

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}