Coverage Report

Created: 2026-06-07 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.29/src/tangent/tanf.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 6/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::polyeval::f_polyeval5;
31
use crate::tangent::evalf::tanf_eval;
32
33
#[inline(always)]
34
0
fn tanf_gen_impl(x: f32) -> f32 {
35
0
    let x_abs = x.to_bits() & 0x7fff_ffffu32;
36
0
    let xd = x as f64;
37
38
    // |x| < pi/32
39
0
    if x_abs <= 0x3dc9_0fdbu32 {
40
        // |x| < 0.000244141
41
0
        if x_abs < 0x3980_0000u32 {
42
0
            if x_abs == 0 {
43
0
                return x;
44
0
            }
45
46
            // When |x| < 2^-12, the relative error of the approximation tan(x) ~ x
47
            // is:
48
            //   |tan(x) - x| / |tan(x)| < |x^3| / (3|x|)
49
            //                           = x^2 / 3
50
            //                           < 2^-25
51
            //                           < epsilon(1)/2.
52
            #[cfg(any(
53
                all(
54
                    any(target_arch = "x86", target_arch = "x86_64"),
55
                    target_feature = "fma"
56
                ),
57
                target_arch = "aarch64"
58
            ))]
59
            {
60
                use crate::common::f_fmlaf;
61
                return f_fmlaf(x, f32::from_bits(0xb3000000), x);
62
            }
63
            #[cfg(not(any(
64
                all(
65
                    any(target_arch = "x86", target_arch = "x86_64"),
66
                    target_feature = "fma"
67
                ),
68
                target_arch = "aarch64"
69
            )))]
70
            {
71
0
                return f_fmla(xd, f64::from_bits(0xbe60000000000000), xd) as f32;
72
            }
73
0
        }
74
75
0
        let xsqr = xd * xd;
76
77
        /*
78
           Generated by Sollya:
79
           f_tan = tan(x)/x;
80
           Q = fpminimax(f_tan, [|0, 2, 4, 6, 8|], [|1, D...|], [0, pi/32]);
81
82
           See ./notes/tanf_at_zero.sollya
83
        */
84
0
        let p = f_polyeval5(
85
0
            xsqr,
86
0
            f64::from_bits(0x3ff0000000000000),
87
0
            f64::from_bits(0x3fd555555553d022),
88
0
            f64::from_bits(0x3fc111111ce442c1),
89
0
            f64::from_bits(0x3faba180a6bbdecd),
90
0
            f64::from_bits(0x3f969c0a88a0b71f),
91
        );
92
0
        return (xd * p) as f32;
93
0
    }
94
95
0
    if x_abs >= 0x7f80_0000u32 {
96
0
        return x + f32::NAN;
97
0
    }
98
99
    // For |x| >= pi/32, we use the definition of tan(x) function:
100
    // tan(a+b) = (tan(a) + tan(b)) / (1 - tan(a)tan(b))
101
    // tanf_eval returns:
102
    // - rs.tan_y = tan(pi/32 * y)          -> tangent of the remainder
103
    // - rs.tan_k = tan(pi/32 * k)          -> tan of the main angle multiple
104
0
    let rs = tanf_eval(xd, x_abs);
105
106
    // Then computing tan through identities
107
    // num = tan(k*pi/32) + tan(y*pi/32)
108
0
    let num = rs.tan_y + rs.tan_k;
109
    // den = 1 - tan(k*pi/32) * tan(y*pi/32)
110
0
    let den = f_fmla(rs.tan_y, -rs.tan_k, 1.);
111
0
    (num / den) as f32
112
0
}
113
114
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
115
#[target_feature(enable = "avx", enable = "fma")]
116
0
unsafe fn tanf_fma_impl(x: f32) -> f32 {
117
0
    let x_abs = x.to_bits() & 0x7fff_ffffu32;
118
0
    let xd = x as f64;
119
120
    // |x| < pi/32
121
0
    if x_abs <= 0x3dc9_0fdbu32 {
122
        // |x| < 0.000244141
123
0
        if x_abs < 0x3980_0000u32 {
124
0
            if x_abs == 0 {
125
0
                return x;
126
0
            }
127
128
            // When |x| < 2^-12, the relative error of the approximation tan(x) ~ x
129
            // is:
130
            //   |tan(x) - x| / |tan(x)| < |x^3| / (3|x|)
131
            //                           = x^2 / 3
132
            //                           < 2^-25
133
            //                           < epsilon(1)/2.
134
0
            return f32::mul_add(x, f32::from_bits(0xb3000000), x);
135
0
        }
136
137
0
        let xsqr = xd * xd;
138
139
        /*
140
           Generated by Sollya:
141
           f_tan = tan(x)/x;
142
           Q = fpminimax(f_tan, [|0, 2, 4, 6, 8|], [|1, D...|], [0, pi/32]);
143
144
           See ./notes/tanf_at_zero.sollya
145
        */
146
        use crate::polyeval::d_polyeval5;
147
0
        let p = d_polyeval5(
148
0
            xsqr,
149
0
            f64::from_bits(0x3ff0000000000000),
150
0
            f64::from_bits(0x3fd555555553d022),
151
0
            f64::from_bits(0x3fc111111ce442c1),
152
0
            f64::from_bits(0x3faba180a6bbdecd),
153
0
            f64::from_bits(0x3f969c0a88a0b71f),
154
        );
155
0
        return (xd * p) as f32;
156
0
    }
157
158
0
    if x_abs >= 0x7f80_0000u32 {
159
0
        return x + f32::NAN;
160
0
    }
161
162
    // For |x| >= pi/32, we use the definition of tan(x) function:
163
    // tan(a+b) = (tan(a) + tan(b)) / (1 - tan(a)tan(b))
164
    // tanf_eval returns:
165
    // - rs.tan_y = tan(pi/32 * y)          -> tangent of the remainder
166
    // - rs.tan_k = tan(pi/32 * k)          -> tan of the main angle multiple
167
    use crate::tangent::evalf::tanf_eval_fma;
168
0
    let rs = tanf_eval_fma(xd, x_abs);
169
170
    // Then computing tan through identities
171
    // num = tan(k*pi/32) + tan(y*pi/32)
172
0
    let num = rs.tan_y + rs.tan_k;
173
    // den = 1 - tan(k*pi/32) * tan(y*pi/32)
174
0
    let den = f64::mul_add(rs.tan_y, -rs.tan_k, 1.);
175
0
    (num / den) as f32
176
0
}
177
178
/// Computes tan
179
///
180
/// Max found ULP 0.4999999
181
#[inline]
182
0
pub fn f_tanf(x: f32) -> f32 {
183
    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
184
    {
185
        tanf_gen_impl(x)
186
    }
187
    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
188
    {
189
        use std::sync::OnceLock;
190
        static EXECUTOR: OnceLock<unsafe fn(f32) -> f32> = OnceLock::new();
191
0
        let q = EXECUTOR.get_or_init(|| {
192
0
            if std::arch::is_x86_feature_detected!("avx")
193
0
                && std::arch::is_x86_feature_detected!("fma")
194
            {
195
0
                tanf_fma_impl
196
            } else {
197
0
                tanf_gen_impl
198
            }
199
0
        });
200
0
        unsafe { q(x) }
201
    }
202
0
}
203
204
#[cfg(test)]
205
mod tests {
206
    use super::*;
207
208
    #[test]
209
    fn f_tanf_test() {
210
        assert_eq!(f_tanf(0.0), 0.0);
211
        assert_eq!(f_tanf(1.0), 1.5574077);
212
        assert_eq!(f_tanf(-1.0), -1.5574077);
213
        assert_eq!(f_tanf(10.0), 0.64836085);
214
        assert_eq!(f_tanf(-10.0), -0.64836085);
215
    }
216
}