1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
use super::data::LOG10_E; use super::ln::ln; use crate::float::{EPSILON, F}; use crate::utils::{f, nearly_equal, round_small}; /// Computes decimal logarithm of a number. /// /// # Notes /// /// Theoretical input domain is (0, max(f32)] ≈ (0, 3.40282347e+38], but near /// zero the values get quite inaccurate. /// /// # Examples /// /// ``` /// use nikisas::log10; /// assert_eq!(log10(100.0), 2.0); /// ``` /// /// # Implementation details /// /// The following identity is used for computation of log10(x): /// /// ```plain /// log10(x) = ln(x) / ln(10) = ln(x) * log10(e) /// ``` /// /// For computing ln(x) we use [`ln`] routine and log10(e) is precomputed /// constant. /// /// We would like to get exact values when the input number is a power of ten. /// However, in this case it's not that straightforward as in [`pow2`]. We /// fallback to the following faithful determination: If the computed value of /// log10(x) is close to an integer, than we assume that the input was indeed a /// power of ten. Then we return the rounded value. This is not always true /// because the tolerance for "closeness" is a bit bigger than in other cases /// throughout this library. /// /// [`ln`]: fn.ln.html /// [`pow2`]: fn.pow2.html pub fn log10(x: F) -> F { let log10x = ln(x) * f(LOG10_E); let rounded = round_small(log10x) as F; if nearly_equal(log10x, rounded, 16.0 * EPSILON) { rounded } else { log10x } } #[cfg(test)] mod tests { use crate::float::F; use crate::test::error_bounds; use nikisas_test::prelude::*; use nikisas_test::utils::shift_right; #[test] fn log10() { (0..32) .fold(Error::with_bounds(error_bounds()), |mut error, k| { let x = 10.0f32.powi(k); let k = k as F; error.calculate(k, super::log10(x), k); error }) .assert(); UniformSample::with_count(shift_right(0.0), 3.4e+38, 10000) .assert(error_bounds(), |x| (super::log10(x), x.log10())); } }