1 /* 2 * Double-precision vector sinh(x) function. 3 * 4 * Copyright (c) 2022-2024, Arm Limited. 5 * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception 6 */ 7 8 #include "v_math.h" 9 #include "test_sig.h" 10 #include "test_defs.h" 11 #include "v_expm1_inline.h" 12 13 static const struct data 14 { 15 struct v_expm1_data d; 16 uint64x2_t halff; 17 #if WANT_SIMD_EXCEPT 18 uint64x2_t tiny_bound, thresh; 19 #else 20 float64x2_t large_bound; 21 #endif 22 } data = { 23 .d = V_EXPM1_DATA, 24 .halff = V2 (0x3fe0000000000000), 25 #if WANT_SIMD_EXCEPT 26 /* 2^-26, below which sinh(x) rounds to x. */ 27 .tiny_bound = V2 (0x3e50000000000000), 28 /* asuint(large_bound) - asuint(tiny_bound). */ 29 .thresh = V2 (0x0230000000000000), 30 #else 31 /* 2^9. expm1 helper overflows for large input. */ 32 .large_bound = V2 (0x1p+9), 33 #endif 34 }; 35 36 static float64x2_t NOINLINE VPCS_ATTR 37 special_case (float64x2_t x) 38 { 39 return v_call_f64 (sinh, x, x, v_u64 (-1)); 40 } 41 42 /* Approximation for vector double-precision sinh(x) using expm1. 43 sinh(x) = (exp(x) - exp(-x)) / 2. 44 The greatest observed error is 2.52 ULP: 45 _ZGVnN2v_sinh(-0x1.a098a2177a2b9p-2) got -0x1.ac2f05bb66fccp-2 46 want -0x1.ac2f05bb66fc9p-2. */ 47 float64x2_t VPCS_ATTR V_NAME_D1 (sinh) (float64x2_t x) 48 { 49 const struct data *d = ptr_barrier (&data); 50 51 float64x2_t ax = vabsq_f64 (x); 52 uint64x2_t ix = vreinterpretq_u64_f64 (x); 53 float64x2_t halfsign = vreinterpretq_f64_u64 ( 54 vbslq_u64 (v_u64 (0x8000000000000000), ix, d->halff)); 55 56 #if WANT_SIMD_EXCEPT 57 uint64x2_t special = vcgeq_u64 ( 58 vsubq_u64 (vreinterpretq_u64_f64 (ax), d->tiny_bound), d->thresh); 59 #else 60 uint64x2_t special = vcageq_f64 (x, d->large_bound); 61 #endif 62 63 /* Fall back to scalar variant for all lanes if any of them are special. */ 64 if (unlikely (v_any_u64 (special))) 65 return special_case (x); 66 67 /* Up to the point that expm1 overflows, we can use it to calculate sinh 68 using a slight rearrangement of the definition of sinh. This allows us to 69 retain acceptable accuracy for very small inputs. */ 70 float64x2_t t = expm1_inline (ax, &d->d); 71 t = vaddq_f64 (t, vdivq_f64 (t, vaddq_f64 (t, v_f64 (1.0)))); 72 return vmulq_f64 (t, halfsign); 73 } 74 75 TEST_SIG (V, D, 1, sinh, -10.0, 10.0) 76 TEST_ULP (V_NAME_D1 (sinh), 2.02) 77 TEST_DISABLE_FENV_IF_NOT (V_NAME_D1 (sinh), WANT_SIMD_EXCEPT) 78 TEST_SYM_INTERVAL (V_NAME_D1 (sinh), 0, 0x1p-26, 1000) 79 TEST_SYM_INTERVAL (V_NAME_D1 (sinh), 0x1p-26, 0x1p9, 500000) 80 TEST_SYM_INTERVAL (V_NAME_D1 (sinh), 0x1p9, inf, 1000) 81