1 /* 2 * Single-precision vector log(1+x) function. 3 * 4 * Copyright (c) 2022-2023, Arm Limited. 5 * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception 6 */ 7 8 #include "v_math.h" 9 #include "pl_sig.h" 10 #include "pl_test.h" 11 12 #if V_SUPPORTED 13 14 #define AbsMask 0x7fffffff 15 #define TinyBound 0x340 /* asuint32(0x1p-23). ulp=0.5 at 0x1p-23. */ 16 #define MinusOne 0xbf800000 17 #define Ln2 (0x1.62e43p-1f) 18 #define Four 0x40800000 19 #define ThreeQuarters v_u32 (0x3f400000) 20 21 #define C(i) v_f32 (__log1pf_data.coeffs[i]) 22 23 static inline v_f32_t 24 eval_poly (v_f32_t m) 25 { 26 #ifdef V_LOG1PF_1U3 27 28 /* Approximate log(1+m) on [-0.25, 0.5] using Horner scheme. */ 29 v_f32_t p = v_fma_f32 (C (8), m, C (7)); 30 p = v_fma_f32 (p, m, C (6)); 31 p = v_fma_f32 (p, m, C (5)); 32 p = v_fma_f32 (p, m, C (4)); 33 p = v_fma_f32 (p, m, C (3)); 34 p = v_fma_f32 (p, m, C (2)); 35 p = v_fma_f32 (p, m, C (1)); 36 p = v_fma_f32 (p, m, C (0)); 37 return v_fma_f32 (m, m * p, m); 38 39 #elif defined(V_LOG1PF_2U5) 40 41 /* Approximate log(1+m) on [-0.25, 0.5] using Estrin scheme. */ 42 v_f32_t p_12 = v_fma_f32 (m, C (1), C (0)); 43 v_f32_t p_34 = v_fma_f32 (m, C (3), C (2)); 44 v_f32_t p_56 = v_fma_f32 (m, C (5), C (4)); 45 v_f32_t p_78 = v_fma_f32 (m, C (7), C (6)); 46 47 v_f32_t m2 = m * m; 48 v_f32_t p_02 = v_fma_f32 (m2, p_12, m); 49 v_f32_t p_36 = v_fma_f32 (m2, p_56, p_34); 50 v_f32_t p_79 = v_fma_f32 (m2, C (8), p_78); 51 52 v_f32_t m4 = m2 * m2; 53 v_f32_t p_06 = v_fma_f32 (m4, p_36, p_02); 54 55 return v_fma_f32 (m4, m4 * p_79, p_06); 56 57 #else 58 #error No precision specified for v_log1pf 59 #endif 60 } 61 62 static inline float 63 handle_special (float x) 64 { 65 uint32_t ix = asuint (x); 66 uint32_t ia = ix & AbsMask; 67 if (ix == 0xff800000 || ia > 0x7f800000 || ix > 0xbf800000) 68 { 69 /* x == -Inf => log1pf(x) = NaN. 70 x < -1.0 => log1pf(x) = NaN. 71 x == +/-NaN => log1pf(x) = NaN. */ 72 #if WANT_SIMD_EXCEPT 73 return __math_invalidf (asfloat (ia)); 74 #else 75 return NAN; 76 #endif 77 } 78 if (ix == 0xbf800000) 79 { 80 /* x == -1.0 => log1pf(x) = -Inf. */ 81 #if WANT_SIMD_EXCEPT 82 return __math_divzerof (ix); 83 #else 84 return -INFINITY; 85 #endif 86 } 87 /* |x| < TinyBound => log1p(x) = x. */ 88 return x; 89 } 90 91 /* Vector log1pf approximation using polynomial on reduced interval. Accuracy is 92 the same as for the scalar algorithm, i.e. worst-case error when using Estrin 93 is roughly 2.02 ULP: 94 log1pf(0x1.21e13ap-2) got 0x1.fe8028p-3 want 0x1.fe802cp-3. */ 95 VPCS_ATTR v_f32_t V_NAME (log1pf) (v_f32_t x) 96 { 97 v_u32_t ix = v_as_u32_f32 (x); 98 v_u32_t ia12 = (ix >> 20) & v_u32 (0x7f8); 99 v_u32_t special_cases 100 = v_cond_u32 (ia12 - v_u32 (TinyBound) >= (0x7f8 - TinyBound)) 101 | v_cond_u32 (ix >= MinusOne); 102 v_f32_t special_arg = x; 103 104 #if WANT_SIMD_EXCEPT 105 if (unlikely (v_any_u32 (special_cases))) 106 /* Side-step special lanes so fenv exceptions are not triggered 107 inadvertently. */ 108 x = v_sel_f32 (special_cases, v_f32 (1), x); 109 #endif 110 111 /* With x + 1 = t * 2^k (where t = m + 1 and k is chosen such that m 112 is in [-0.25, 0.5]): 113 log1p(x) = log(t) + log(2^k) = log1p(m) + k*log(2). 114 115 We approximate log1p(m) with a polynomial, then scale by 116 k*log(2). Instead of doing this directly, we use an intermediate 117 scale factor s = 4*k*log(2) to ensure the scale is representable 118 as a normalised fp32 number. */ 119 120 v_f32_t m = x + v_f32 (1.0f); 121 122 /* Choose k to scale x to the range [-1/4, 1/2]. */ 123 v_s32_t k = (v_as_s32_f32 (m) - ThreeQuarters) & v_u32 (0xff800000); 124 125 /* Scale x by exponent manipulation. */ 126 v_f32_t m_scale = v_as_f32_u32 (v_as_u32_f32 (x) - v_as_u32_s32 (k)); 127 128 /* Scale up to ensure that the scale factor is representable as normalised 129 fp32 number, and scale m down accordingly. */ 130 v_f32_t s = v_as_f32_u32 (v_u32 (Four) - k); 131 m_scale = m_scale + v_fma_f32 (v_f32 (0.25f), s, v_f32 (-1.0f)); 132 133 /* Evaluate polynomial on the reduced interval. */ 134 v_f32_t p = eval_poly (m_scale); 135 136 /* The scale factor to be applied back at the end - by multiplying float(k) 137 by 2^-23 we get the unbiased exponent of k. */ 138 v_f32_t scale_back = v_to_f32_s32 (k) * v_f32 (0x1p-23f); 139 140 /* Apply the scaling back. */ 141 v_f32_t y = v_fma_f32 (scale_back, v_f32 (Ln2), p); 142 143 if (unlikely (v_any_u32 (special_cases))) 144 return v_call_f32 (handle_special, special_arg, y, special_cases); 145 return y; 146 } 147 VPCS_ALIAS 148 149 PL_SIG (V, F, 1, log1p, -0.9, 10.0) 150 PL_TEST_ULP (V_NAME (log1pf), 1.53) 151 PL_TEST_EXPECT_FENV (V_NAME (log1pf), WANT_SIMD_EXCEPT) 152 PL_TEST_INTERVAL (V_NAME (log1pf), -10.0, 10.0, 10000) 153 PL_TEST_INTERVAL (V_NAME (log1pf), 0.0, 0x1p-23, 30000) 154 PL_TEST_INTERVAL (V_NAME (log1pf), 0x1p-23, 0.001, 50000) 155 PL_TEST_INTERVAL (V_NAME (log1pf), 0.001, 1.0, 50000) 156 PL_TEST_INTERVAL (V_NAME (log1pf), 0.0, -0x1p-23, 30000) 157 PL_TEST_INTERVAL (V_NAME (log1pf), -0x1p-23, -0.001, 30000) 158 PL_TEST_INTERVAL (V_NAME (log1pf), -0.001, -1.0, 50000) 159 PL_TEST_INTERVAL (V_NAME (log1pf), -1.0, inf, 1000) 160 #endif 161