/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.25/src/tangent/cotpi.rs
Line | Count | Source |
1 | | /* |
2 | | * // Copyright (c) Radzivon Bartoshyk 8/2025. All rights reserved. |
3 | | * // |
4 | | * // Redistribution and use in source and binary forms, with or without modification, |
5 | | * // are permitted provided that the following conditions are met: |
6 | | * // |
7 | | * // 1. Redistributions of source code must retain the above copyright notice, this |
8 | | * // list of conditions and the following disclaimer. |
9 | | * // |
10 | | * // 2. Redistributions in binary form must reproduce the above copyright notice, |
11 | | * // this list of conditions and the following disclaimer in the documentation |
12 | | * // and/or other materials provided with the distribution. |
13 | | * // |
14 | | * // 3. Neither the name of the copyright holder nor the names of its |
15 | | * // contributors may be used to endorse or promote products derived from |
16 | | * // this software without specific prior written permission. |
17 | | * // |
18 | | * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
19 | | * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 | | * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
21 | | * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
22 | | * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 | | * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
24 | | * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
25 | | * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
26 | | * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
27 | | * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | | */ |
29 | | use crate::common::f_fmla; |
30 | | use crate::double_double::DoubleDouble; |
31 | | use crate::sincospi::reduce_pi_64; |
32 | | use crate::tangent::tanpi::tanpi_eval; |
33 | | use crate::tangent::tanpi_table::TANPI_K_PI_OVER_64; |
34 | | |
35 | | #[cold] |
36 | 0 | fn cotpi_hard(x: f64, tan_k: DoubleDouble) -> DoubleDouble { |
37 | | const C: [(u64, u64); 6] = [ |
38 | | (0x3ca1a62632712fc8, 0x400921fb54442d18), |
39 | | (0xbcc052338fbb4528, 0x4024abbce625be53), |
40 | | (0x3ced42454c5f85b3, 0x404466bc6775aad9), |
41 | | (0xbd00c7d6a971a560, 0x40645fff9b4b244d), |
42 | | (0x3d205970eff53274, 0x40845f46e96c3a0b), |
43 | | (0xbd3589489ad24fc4, 0x40a4630551cd123d), |
44 | | ]; |
45 | 0 | let x2 = DoubleDouble::from_exact_mult(x, x); |
46 | 0 | let mut tan_y = DoubleDouble::quick_mul_add( |
47 | 0 | x2, |
48 | 0 | DoubleDouble::from_bit_pair(C[5]), |
49 | 0 | DoubleDouble::from_bit_pair(C[4]), |
50 | | ); |
51 | 0 | tan_y = DoubleDouble::quick_mul_add(x2, tan_y, DoubleDouble::from_bit_pair(C[3])); |
52 | 0 | tan_y = DoubleDouble::quick_mul_add(x2, tan_y, DoubleDouble::from_bit_pair(C[2])); |
53 | 0 | tan_y = DoubleDouble::quick_mul_add(x2, tan_y, DoubleDouble::from_bit_pair(C[1])); |
54 | 0 | tan_y = DoubleDouble::quick_mul_add(x2, tan_y, DoubleDouble::from_bit_pair(C[0])); |
55 | 0 | tan_y = DoubleDouble::quick_mult_f64(tan_y, x); |
56 | | |
57 | | // num = tan(y*pi/64) + tan(k*pi/64) |
58 | 0 | let num = DoubleDouble::full_dd_add(tan_y, tan_k); |
59 | | // den = 1 - tan(y*pi/64)*tan(k*pi/64) |
60 | 0 | let den = DoubleDouble::mul_add_f64(tan_y, -tan_k, 1.); |
61 | | // cot = den / num |
62 | 0 | DoubleDouble::div(den, num) |
63 | 0 | } |
64 | | |
65 | | /// Computes cotangent 1/tan(PI*x) |
66 | | /// |
67 | | /// ulp 0.5 |
68 | 0 | pub fn f_cotpi(x: f64) -> f64 { |
69 | 0 | if x == 0. { |
70 | 0 | return if x.is_sign_negative() { |
71 | 0 | f64::NEG_INFINITY |
72 | | } else { |
73 | 0 | f64::INFINITY |
74 | | }; |
75 | 0 | } |
76 | 0 | let ax = x.to_bits() & 0x7fff_ffff_ffff_ffff; |
77 | 0 | if ax >= (0x7ffu64 << 52) { |
78 | | // NaN, Inf |
79 | 0 | if ax > (0x7ffu64 << 52) { |
80 | 0 | return x + x; |
81 | 0 | } // NaN |
82 | 0 | return f64::NAN; // x=Inf |
83 | 0 | } |
84 | 0 | let e: i32 = (ax >> 52) as i32 - 1023; |
85 | 0 | if e > 0 { |
86 | 0 | if e >= 52 { |
87 | | // when |x| > 2^53 it's always an integer |
88 | 0 | return f64::copysign(f64::INFINITY, x); |
89 | 0 | } |
90 | | // |x| > 1 and |x| < 2^53 |
91 | 0 | let m = (ax & ((1u64 << 52) - 1)) | (1u64 << 52); // mantissa with hidden 1 |
92 | 0 | let shift = 52 - e; |
93 | | |
94 | 0 | let frac = m & ((1u64 << shift) - 1); |
95 | 0 | if frac == (1u64 << (shift - 1)) { |
96 | | // |x| is always integer.5 means it's inf |
97 | 0 | return f64::copysign(0., x); |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | 0 | if ax <= 0x3cb0000000000000 { |
102 | | // for tiny x ( |x| < f64::EPSILON ) just small taylor expansion |
103 | | // cot(PI*x) ~ 1/(PI*x) + O(x^3) |
104 | | const ONE_OVER_PI: DoubleDouble = |
105 | | DoubleDouble::from_bit_pair((0xbc76b01ec5417056, 0x3fd45f306dc9c883)); |
106 | 0 | if ax <= 0x3ca0000000000000 { |
107 | | // |x| <= 2^-53, renormalize value |
108 | 0 | let e: i32 = (ax >> 52) as i32; |
109 | 0 | let sc = f64::from_bits((2045i64 - e as i64).wrapping_shl(52) as u64); |
110 | 0 | let dx = x * sc; |
111 | 0 | let q0 = DoubleDouble::quick_mult(ONE_OVER_PI, DoubleDouble::from_quick_recip(dx)); |
112 | 0 | let r = q0.to_f64() * sc; |
113 | 0 | return r; |
114 | 0 | } |
115 | 0 | let q0 = DoubleDouble::quick_mult(ONE_OVER_PI, DoubleDouble::from_quick_recip(x)); |
116 | 0 | let r = q0.to_f64(); |
117 | 0 | return r; |
118 | 0 | } |
119 | | |
120 | | // argument reduction |
121 | 0 | let (y, k) = reduce_pi_64(x); |
122 | | |
123 | 0 | if y == 0.0 { |
124 | 0 | let km = (k.abs() & 63) as i32; // k mod 64 |
125 | | |
126 | 0 | match km { |
127 | 0 | 0 => return f64::copysign(f64::INFINITY, x), // cotpi(n) = 0 |
128 | 0 | 32 => return f64::copysign(0., x), // cotpi(n+0.5) = ±∞ |
129 | 0 | 16 => return f64::copysign(1.0, x), // cotpi(n+0.25) = ±1 |
130 | 0 | 48 => return -f64::copysign(1.0, x), // cotpi(n+0.75) = ∓1 |
131 | 0 | _ => {} |
132 | | } |
133 | 0 | } |
134 | | |
135 | 0 | let tan_k = DoubleDouble::from_bit_pair(TANPI_K_PI_OVER_64[((k as u64) & 127) as usize]); |
136 | | |
137 | | // Computes tan(pi*x) through identities |
138 | | // tan(a+b) = (tan(a) + tan(b)) / (1 - tan(a)tan(b)) = (tan(y*pi/64) + tan(k*pi/64)) / (1 - tan(y*pi/64)*tan(k*pi/64)) |
139 | 0 | let tan_y = tanpi_eval(y); |
140 | | // num = tan(y*pi/64) + tan(k*pi/64) |
141 | 0 | let num = DoubleDouble::add(tan_k, tan_y); |
142 | | // den = 1 - tan(y*pi/64)*tan(k*pi/64) |
143 | 0 | let den = DoubleDouble::mul_add_f64(tan_y, -tan_k, 1.); |
144 | | // cot = den / num |
145 | 0 | let tan_value = DoubleDouble::div(den, num); |
146 | | |
147 | 0 | let err = f_fmla( |
148 | 0 | tan_value.hi, |
149 | 0 | f64::from_bits(0x3bf0000000000000), // 2^-64 |
150 | 0 | f64::from_bits(0x3b60000000000000), // 2^-73 |
151 | | ); |
152 | 0 | let ub = tan_value.hi + (tan_value.lo + err); |
153 | 0 | let lb = tan_value.hi + (tan_value.lo - err); |
154 | 0 | if ub == lb { |
155 | 0 | return tan_value.to_f64(); |
156 | 0 | } |
157 | 0 | cotpi_hard(y, tan_k).to_f64() |
158 | 0 | } |
159 | | |
160 | | #[inline] |
161 | 0 | pub(crate) fn cotpi_core(x: f64) -> DoubleDouble { |
162 | | // argument reduction |
163 | 0 | let (y, k) = reduce_pi_64(x); |
164 | | |
165 | 0 | let tan_k = DoubleDouble::from_bit_pair(TANPI_K_PI_OVER_64[((k as u64) & 127) as usize]); |
166 | | |
167 | | // Computes tan(pi*x) through identities. |
168 | | // tan(a+b) = (tan(a) + tan(b)) / (1 - tan(a)tan(b)) = (tan(y*pi/64) + tan(k*pi/64)) / (1 - tan(y*pi/64)*tan(k*pi/64)) |
169 | 0 | let tan_y = tanpi_eval(y); |
170 | | // num = tan(y*pi/64) + tan(k*pi/64) |
171 | 0 | let num = DoubleDouble::add(tan_k, tan_y); |
172 | | // den = 1 - tan(y*pi/64)*tan(k*pi/64) |
173 | 0 | let den = DoubleDouble::mul_add_f64(tan_y, -tan_k, 1.); |
174 | | // cot = den / num |
175 | 0 | DoubleDouble::div(den, num) |
176 | 0 | } |
177 | | |
178 | | #[cfg(test)] |
179 | | mod tests { |
180 | | use super::*; |
181 | | |
182 | | #[test] |
183 | | fn test_cotpi() { |
184 | | assert_eq!(f_cotpi(3.382112265043898e-306), 9.411570676518013e304); |
185 | | assert_eq!(f_cotpi(0.0431431231), 7.332763436038805); |
186 | | assert_eq!(f_cotpi(-0.0431431231), -7.332763436038805); |
187 | | assert_eq!(f_cotpi(0.52324), -0.07314061937774036); |
188 | | assert!(f_cotpi(f64::INFINITY).is_nan()); |
189 | | assert!(f_cotpi(f64::NAN).is_nan()); |
190 | | assert!(f_cotpi(f64::NEG_INFINITY).is_nan()); |
191 | | } |
192 | | } |