Coverage Report

Created: 2026-07-16 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.30/src/logs/log10p1f.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 7/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::logs::LOG_RANGE_REDUCTION;
31
use crate::polyeval::{f_estrin_polyeval7, f_polyeval6};
32
33
// Generated by SageMath:
34
// print("[")
35
// for i in range(128):
36
//     R = RealField(200)
37
//     r = R(2)**(-8) * ( R(2)**8 * (R(1) - R(2)**(-8)) / (R(1) + R(i)*R(2)**-7) ).ceil()
38
//
39
//     if i == 0 or i == 127:
40
//         print(double_to_hex(0), ",")
41
//     else:
42
//         print(double_to_hex(-r.log10()), ",")
43
// print("];")
44
static LOG10_D: [u64; 128] = [
45
    0x0000000000000000,
46
    0x3f6be76bd77b4fc3,
47
    0x3f7c03a80ae5e054,
48
    0x3f851824c7587eb0,
49
    0x3f8c3d0837784c41,
50
    0x3f91b85d6044e9ae,
51
    0x3f9559bd2406c3ba,
52
    0x3f9902c31d62a843,
53
    0x3f9cb38fccd8bfdb,
54
    0x3f9e8eeb09f2f6cb,
55
    0x3fa125d0432ea20e,
56
    0x3fa30838cdc2fbfd,
57
    0x3fa3faf7c663060e,
58
    0x3fa5e3966b7e9295,
59
    0x3fa7d070145f4fd7,
60
    0x3fa8c878eeb05074,
61
    0x3faabbcebd84fca0,
62
    0x3fabb7209d1e24e5,
63
    0x3fadb11ed766abf4,
64
    0x3faeafd05035bd3b,
65
    0x3fb0585283764178,
66
    0x3fb0d966cc6500fa,
67
    0x3fb1dd5460c8b16f,
68
    0x3fb2603072a25f82,
69
    0x3fb367ba3aaa1883,
70
    0x3fb3ec6ad5407868,
71
    0x3fb4f7aad9bbcbaf,
72
    0x3fb57e3d47c3af7b,
73
    0x3fb605735ee985f1,
74
    0x3fb715d0ce367afc,
75
    0x3fb79efb57b0f803,
76
    0x3fb828cfed29a215,
77
    0x3fb93e7de0fc3e80,
78
    0x3fb9ca5aa1729f45,
79
    0x3fba56e8325f5c87,
80
    0x3fbae4285509950b,
81
    0x3fbb721cd17157e3,
82
    0x3fbc902a19e65111,
83
    0x3fbd204698cb42bd,
84
    0x3fbdb11ed766abf4,
85
    0x3fbe42b4c16caaf3,
86
    0x3fbed50a4a26eafc,
87
    0x3fbffbfc2bbc7803,
88
    0x3fc0484e4942aa43,
89
    0x3fc093025a19976c,
90
    0x3fc0de1b56356b04,
91
    0x3fc1299a4fb3e306,
92
    0x3fc175805d1587c1,
93
    0x3fc1c1ce9955c0c6,
94
    0x3fc20e8624038fed,
95
    0x3fc25ba8215af7fc,
96
    0x3fc2a935ba5f1479,
97
    0x3fc2f7301cf4e87b,
98
    0x3fc345987bfeea91,
99
    0x3fc394700f7953fd,
100
    0x3fc3e3b8149739d4,
101
    0x3fc43371cde076c2,
102
    0x3fc4839e83506c87,
103
    0x3fc4d43f8275a483,
104
    0x3fc525561e9256ee,
105
    0x3fc576e3b0bde0a7,
106
    0x3fc5c8e998072fe2,
107
    0x3fc61b6939983048,
108
    0x3fc66e6400da3f77,
109
    0x3fc6c1db5f9bb336,
110
    0x3fc6c1db5f9bb336,
111
    0x3fc715d0ce367afc,
112
    0x3fc76a45cbb7e6ff,
113
    0x3fc7bf3bde099f30,
114
    0x3fc814b4921bd52b,
115
    0x3fc86ab17c10bc7f,
116
    0x3fc86ab17c10bc7f,
117
    0x3fc8c13437695532,
118
    0x3fc9183e673394fa,
119
    0x3fc96fd1b639fc09,
120
    0x3fc9c7efd734a2f9,
121
    0x3fca209a84fbcff8,
122
    0x3fca209a84fbcff8,
123
    0x3fca79d382bc21d9,
124
    0x3fcad39c9c2c6080,
125
    0x3fcb2df7a5c50299,
126
    0x3fcb2df7a5c50299,
127
    0x3fcb88e67cf97980,
128
    0x3fcbe46b087354bc,
129
    0x3fcc4087384f4f80,
130
    0x3fcc4087384f4f80,
131
    0x3fcc9d3d065c5b42,
132
    0x3fccfa8e765cbb72,
133
    0x3fccfa8e765cbb72,
134
    0x3fcd587d96494759,
135
    0x3fcdb70c7e96e7f3,
136
    0x3fcdb70c7e96e7f3,
137
    0x3fce163d527e68cf,
138
    0x3fce76124046b3f3,
139
    0x3fce76124046b3f3,
140
    0x3fced68d819191fc,
141
    0x3fcf37b15bab08d1,
142
    0x3fcf37b15bab08d1,
143
    0x3fcf99801fdb749d,
144
    0x3fcffbfc2bbc7803,
145
    0x3fcffbfc2bbc7803,
146
    0x3fd02f93f4c87101,
147
    0x3fd06182e84fd4ac,
148
    0x3fd06182e84fd4ac,
149
    0x3fd093cc32c90f84,
150
    0x3fd093cc32c90f84,
151
    0x3fd0c6711d6abd7a,
152
    0x3fd0f972f87ff3d6,
153
    0x3fd0f972f87ff3d6,
154
    0x3fd12cd31b9c99ff,
155
    0x3fd12cd31b9c99ff,
156
    0x3fd16092e5d3a9a6,
157
    0x3fd194b3bdef6b9e,
158
    0x3fd194b3bdef6b9e,
159
    0x3fd1c93712abc7ff,
160
    0x3fd1c93712abc7ff,
161
    0x3fd1fe1e5af2c141,
162
    0x3fd1fe1e5af2c141,
163
    0x3fd2336b161b3337,
164
    0x3fd2336b161b3337,
165
    0x3fd2691ecc29f042,
166
    0x3fd2691ecc29f042,
167
    0x3fd29f3b0e15584b,
168
    0x3fd29f3b0e15584b,
169
    0x3fd2d5c1760b86bb,
170
    0x3fd2d5c1760b86bb,
171
    0x3fd30cb3a7bb3625,
172
    0x0000000000000000,
173
];
174
175
#[inline]
176
0
pub(crate) fn core_log10f(x: f64) -> f64 {
177
0
    let x_u = x.to_bits();
178
179
    const E_BIAS: u64 = (1u64 << (11 - 1u64)) - 1u64;
180
181
0
    let mut x_e: i32 = -(E_BIAS as i32);
182
183
    // log2(x) = log2(2^x_e * x_m)
184
    //         = x_e + log2(x_m)
185
    // Range reduction for log2(x_m):
186
    // For each x_m, we would like to find r such that:
187
    //   -2^-8 <= r * x_m - 1 < 2^-7
188
0
    let shifted = (x_u >> 45) as i32;
189
0
    let index = shifted & 0x7F;
190
0
    let r = f64::from_bits(LOG_RANGE_REDUCTION[index as usize]);
191
192
    // Add unbiased exponent. Add an extra 1 if the 8 leading fractional bits are
193
    // all 1's.
194
0
    x_e = x_e.wrapping_add(x_u.wrapping_add(1u64 << 45).wrapping_shr(52) as i32);
195
0
    let e_x = x_e as f64;
196
197
    const LOG_10_2_HI: f64 = f64::from_bits(0x3fd34413509f79ff);
198
199
0
    let log_r_dd = LOG10_D[index as usize];
200
201
    // hi is exact
202
0
    let hi = f_fmla(e_x, LOG_10_2_HI, f64::from_bits(log_r_dd));
203
204
    // Set m = 1.mantissa.
205
0
    let x_m = (x_u & 0x000F_FFFF_FFFF_FFFFu64) | 0x3FF0_0000_0000_0000u64;
206
0
    let m = f64::from_bits(x_m);
207
208
    let u;
209
    #[cfg(any(
210
        all(
211
            any(target_arch = "x86", target_arch = "x86_64"),
212
            target_feature = "fma"
213
        ),
214
        target_arch = "aarch64"
215
    ))]
216
    {
217
        u = f_fmla(r, m, -1.0); // exact
218
    }
219
    #[cfg(not(any(
220
        all(
221
            any(target_arch = "x86", target_arch = "x86_64"),
222
            target_feature = "fma"
223
        ),
224
        target_arch = "aarch64"
225
    )))]
226
    {
227
        use crate::logs::LOG_CD;
228
0
        let c_m = x_m & 0x3FFF_E000_0000_0000u64;
229
0
        let c = f64::from_bits(c_m);
230
0
        u = f_fmla(r, m - c, f64::from_bits(LOG_CD[index as usize])); // exact
231
    }
232
233
    // Polynomial for log(1+x)/x generated in Sollya:
234
    // d = [-2^-8, 2^-7];
235
    // f_log10 = log10(1 + x)/x;
236
    // Q = fpminimax(f_log10, 6, [|D...|], d);
237
    // See ./notes/log10pf_core.sollya
238
0
    let p = f_polyeval6(
239
0
        u,
240
0
        f64::from_bits(0x3fdbcb7b1526e50e),
241
0
        f64::from_bits(0xbfcbcb7b1526e4e2),
242
0
        f64::from_bits(0x3fc287a763707f60),
243
0
        f64::from_bits(0xbfbbcb7b16f1858d),
244
0
        f64::from_bits(0x3fb63c613ca2ee0f),
245
0
        f64::from_bits(0xbfb28617a50029a9),
246
    );
247
0
    f_fmla(p, u, hi)
248
0
}
249
250
/// Computes log10(x+1)
251
///
252
/// Max ULP 0.5
253
#[inline]
254
0
pub fn f_log10p1f(x: f32) -> f32 {
255
0
    let z = x as f64;
256
0
    let ux = x.to_bits().wrapping_shl(1);
257
0
    if ux >= 0xffu32 << 24 || ux == 0 {
258
        // |x| == 0, |x| == inf, x == NaN
259
0
        if ux == 0 {
260
0
            return x;
261
0
        }
262
0
        if x.is_infinite() {
263
0
            return if x.is_sign_positive() {
264
0
                f32::INFINITY
265
            } else {
266
0
                f32::NAN
267
            };
268
0
        }
269
0
        return x + f32::NAN;
270
0
    }
271
272
0
    let ax = x.to_bits() & 0x7fff_ffffu32;
273
274
    // Use log10p1(x) = log10(1 + x) for |x| > 2^-6;
275
0
    if ax > 0x3c80_0000u32 {
276
0
        if x == -1. {
277
0
            return f32::NEG_INFINITY;
278
0
        }
279
0
        let x1p = z + 1.;
280
0
        if x1p <= 0. {
281
0
            if x1p == 0. {
282
0
                return f32::NEG_INFINITY;
283
0
            }
284
0
            return f32::NAN;
285
0
        }
286
0
        return core_log10f(x1p) as f32;
287
0
    }
288
289
    // log10p1 is expected to be used near zero:
290
    // Polynomial generated by Sollya:
291
    // d = [-2^-6; 2^-6];
292
    // f_log10pf = log10(1+x)/x;
293
    // Q = fpminimax(f_log10pf, 6, [|D...|], d);
294
    const C: [u64; 7] = [
295
        0x3fdbcb7b1526e50e,
296
        0xbfcbcb7b1526f138,
297
        0x3fc287a7636f8798,
298
        0xbfbbcb7b08fcfcad,
299
        0x3fb63c625d2472a4,
300
        0xbfb2892c9b620de1,
301
        0x3fafc7d3af2f4b6c,
302
    ];
303
0
    let p = f_estrin_polyeval7(
304
0
        z,
305
0
        f64::from_bits(C[0]),
306
0
        f64::from_bits(C[1]),
307
0
        f64::from_bits(C[2]),
308
0
        f64::from_bits(C[3]),
309
0
        f64::from_bits(C[4]),
310
0
        f64::from_bits(C[5]),
311
0
        f64::from_bits(C[6]),
312
    );
313
0
    (p * z) as f32
314
0
}
315
316
#[cfg(test)]
317
mod tests {
318
    use super::*;
319
320
    #[test]
321
    fn test_log10p1f() {
322
        assert_eq!(f_log10p1f(0.0), 0.0);
323
        assert_eq!(f_log10p1f(1.0), 0.30103);
324
        assert_eq!(f_log10p1f(-0.0432432), -0.019198442);
325
        assert_eq!(f_log10p1f(-0.009874634), -0.0043098135);
326
        assert_eq!(f_log10p1f(-0.000000054233), -2.3553092e-8);
327
        assert_eq!(f_log10p1f(1.2443), 0.35108092);
328
        assert_eq!(f_log10p1f(f32::INFINITY), f32::INFINITY);
329
        assert!(f_log10p1f(f32::NEG_INFINITY).is_nan());
330
        assert!(f_log10p1f(-1.0432432).is_nan());
331
    }
332
}