15a02ffc3SAndrew Turner /* 25a02ffc3SAndrew Turner * Double-precision 10^x function. 35a02ffc3SAndrew Turner * 4*f3087befSAndrew Turner * Copyright (c) 2023-2024, Arm Limited. 55a02ffc3SAndrew Turner * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception 65a02ffc3SAndrew Turner */ 75a02ffc3SAndrew Turner 85a02ffc3SAndrew Turner #include "math_config.h" 9*f3087befSAndrew Turner #include "test_defs.h" 10*f3087befSAndrew Turner #include "test_sig.h" 115a02ffc3SAndrew Turner 125a02ffc3SAndrew Turner #define N (1 << EXP_TABLE_BITS) 135a02ffc3SAndrew Turner #define IndexMask (N - 1) 145a02ffc3SAndrew Turner #define OFlowBound 0x1.34413509f79ffp8 /* log10(DBL_MAX). */ 155a02ffc3SAndrew Turner #define UFlowBound -0x1.5ep+8 /* -350. */ 165a02ffc3SAndrew Turner #define SmallTop 0x3c6 /* top12(0x1p-57). */ 175a02ffc3SAndrew Turner #define BigTop 0x407 /* top12(0x1p8). */ 185a02ffc3SAndrew Turner #define Thresh 0x41 /* BigTop - SmallTop. */ 195a02ffc3SAndrew Turner #define Shift __exp_data.shift 205a02ffc3SAndrew Turner #define C(i) __exp_data.exp10_poly[i] 215a02ffc3SAndrew Turner 225a02ffc3SAndrew Turner static double 235a02ffc3SAndrew Turner special_case (uint64_t sbits, double_t tmp, uint64_t ki) 245a02ffc3SAndrew Turner { 255a02ffc3SAndrew Turner double_t scale, y; 265a02ffc3SAndrew Turner 27*f3087befSAndrew Turner if ((ki & 0x80000000) == 0) 285a02ffc3SAndrew Turner { 295a02ffc3SAndrew Turner /* The exponent of scale might have overflowed by 1. */ 305a02ffc3SAndrew Turner sbits -= 1ull << 52; 315a02ffc3SAndrew Turner scale = asdouble (sbits); 325a02ffc3SAndrew Turner y = 2 * (scale + scale * tmp); 335a02ffc3SAndrew Turner return check_oflow (eval_as_double (y)); 345a02ffc3SAndrew Turner } 355a02ffc3SAndrew Turner 365a02ffc3SAndrew Turner /* n < 0, need special care in the subnormal range. */ 375a02ffc3SAndrew Turner sbits += 1022ull << 52; 385a02ffc3SAndrew Turner scale = asdouble (sbits); 395a02ffc3SAndrew Turner y = scale + scale * tmp; 405a02ffc3SAndrew Turner 415a02ffc3SAndrew Turner if (y < 1.0) 425a02ffc3SAndrew Turner { 435a02ffc3SAndrew Turner /* Round y to the right precision before scaling it into the subnormal 445a02ffc3SAndrew Turner range to avoid double rounding that can cause 0.5+E/2 ulp error where 455a02ffc3SAndrew Turner E is the worst-case ulp error outside the subnormal range. So this 465a02ffc3SAndrew Turner is only useful if the goal is better than 1 ulp worst-case error. */ 475a02ffc3SAndrew Turner double_t lo = scale - y + scale * tmp; 485a02ffc3SAndrew Turner double_t hi = 1.0 + y; 495a02ffc3SAndrew Turner lo = 1.0 - hi + y + lo; 505a02ffc3SAndrew Turner y = eval_as_double (hi + lo) - 1.0; 515a02ffc3SAndrew Turner /* Avoid -0.0 with downward rounding. */ 525a02ffc3SAndrew Turner if (WANT_ROUNDING && y == 0.0) 535a02ffc3SAndrew Turner y = 0.0; 545a02ffc3SAndrew Turner /* The underflow exception needs to be signaled explicitly. */ 555a02ffc3SAndrew Turner force_eval_double (opt_barrier_double (0x1p-1022) * 0x1p-1022); 565a02ffc3SAndrew Turner } 575a02ffc3SAndrew Turner y = 0x1p-1022 * y; 585a02ffc3SAndrew Turner 595a02ffc3SAndrew Turner return check_uflow (y); 605a02ffc3SAndrew Turner } 615a02ffc3SAndrew Turner 625a02ffc3SAndrew Turner /* Double-precision 10^x approximation. Largest observed error is ~0.513 ULP. */ 635a02ffc3SAndrew Turner double 645a02ffc3SAndrew Turner exp10 (double x) 655a02ffc3SAndrew Turner { 665a02ffc3SAndrew Turner uint64_t ix = asuint64 (x); 675a02ffc3SAndrew Turner uint32_t abstop = (ix >> 52) & 0x7ff; 685a02ffc3SAndrew Turner 695a02ffc3SAndrew Turner if (unlikely (abstop - SmallTop >= Thresh)) 705a02ffc3SAndrew Turner { 715a02ffc3SAndrew Turner if (abstop - SmallTop >= 0x80000000) 725a02ffc3SAndrew Turner /* Avoid spurious underflow for tiny x. 735a02ffc3SAndrew Turner Note: 0 is common input. */ 745a02ffc3SAndrew Turner return x + 1; 755a02ffc3SAndrew Turner if (abstop == 0x7ff) 765a02ffc3SAndrew Turner return ix == asuint64 (-INFINITY) ? 0.0 : x + 1.0; 775a02ffc3SAndrew Turner if (x >= OFlowBound) 785a02ffc3SAndrew Turner return __math_oflow (0); 795a02ffc3SAndrew Turner if (x < UFlowBound) 805a02ffc3SAndrew Turner return __math_uflow (0); 815a02ffc3SAndrew Turner 825a02ffc3SAndrew Turner /* Large x is special-cased below. */ 835a02ffc3SAndrew Turner abstop = 0; 845a02ffc3SAndrew Turner } 855a02ffc3SAndrew Turner 865a02ffc3SAndrew Turner /* Reduce x: z = x * N / log10(2), k = round(z). */ 875a02ffc3SAndrew Turner double_t z = __exp_data.invlog10_2N * x; 885a02ffc3SAndrew Turner double_t kd; 89*f3087befSAndrew Turner uint64_t ki; 905a02ffc3SAndrew Turner #if TOINT_INTRINSICS 915a02ffc3SAndrew Turner kd = roundtoint (z); 925a02ffc3SAndrew Turner ki = converttoint (z); 935a02ffc3SAndrew Turner #else 945a02ffc3SAndrew Turner kd = eval_as_double (z + Shift); 95*f3087befSAndrew Turner ki = asuint64 (kd); 965a02ffc3SAndrew Turner kd -= Shift; 975a02ffc3SAndrew Turner #endif 985a02ffc3SAndrew Turner 995a02ffc3SAndrew Turner /* r = x - k * log10(2), r in [-0.5, 0.5]. */ 1005a02ffc3SAndrew Turner double_t r = x; 1015a02ffc3SAndrew Turner r = __exp_data.neglog10_2hiN * kd + r; 1025a02ffc3SAndrew Turner r = __exp_data.neglog10_2loN * kd + r; 1035a02ffc3SAndrew Turner 1045a02ffc3SAndrew Turner /* exp10(x) = 2^(k/N) * 2^(r/N). 1055a02ffc3SAndrew Turner Approximate the two components separately. */ 1065a02ffc3SAndrew Turner 1075a02ffc3SAndrew Turner /* s = 2^(k/N), using lookup table. */ 1085a02ffc3SAndrew Turner uint64_t e = ki << (52 - EXP_TABLE_BITS); 1095a02ffc3SAndrew Turner uint64_t i = (ki & IndexMask) * 2; 1105a02ffc3SAndrew Turner uint64_t u = __exp_data.tab[i + 1]; 1115a02ffc3SAndrew Turner uint64_t sbits = u + e; 1125a02ffc3SAndrew Turner 1135a02ffc3SAndrew Turner double_t tail = asdouble (__exp_data.tab[i]); 1145a02ffc3SAndrew Turner 1155a02ffc3SAndrew Turner /* 2^(r/N) ~= 1 + r * Poly(r). */ 1165a02ffc3SAndrew Turner double_t r2 = r * r; 1175a02ffc3SAndrew Turner double_t p = C (0) + r * C (1); 1185a02ffc3SAndrew Turner double_t y = C (2) + r * C (3); 1195a02ffc3SAndrew Turner y = y + r2 * C (4); 1205a02ffc3SAndrew Turner y = p + r2 * y; 1215a02ffc3SAndrew Turner y = tail + y * r; 1225a02ffc3SAndrew Turner 1235a02ffc3SAndrew Turner if (unlikely (abstop == 0)) 1245a02ffc3SAndrew Turner return special_case (sbits, y, ki); 1255a02ffc3SAndrew Turner 1265a02ffc3SAndrew Turner /* Assemble components: 1275a02ffc3SAndrew Turner y = 2^(r/N) * 2^(k/N) 1285a02ffc3SAndrew Turner ~= (y + 1) * s. */ 1295a02ffc3SAndrew Turner double_t s = asdouble (sbits); 1305a02ffc3SAndrew Turner return eval_as_double (s * y + s); 1315a02ffc3SAndrew Turner } 132*f3087befSAndrew Turner 133*f3087befSAndrew Turner #if WANT_EXP10_TESTS 134*f3087befSAndrew Turner TEST_SIG (S, D, 1, exp10, -9.9, 9.9) 135*f3087befSAndrew Turner TEST_ULP (exp10, 0.02) 136*f3087befSAndrew Turner TEST_ULP_NONNEAREST (exp10, 0.5) 137*f3087befSAndrew Turner TEST_SYM_INTERVAL (exp10, 0, 0x1p-47, 5000) 138*f3087befSAndrew Turner TEST_SYM_INTERVAL (exp10, 0x1p47, 1, 50000) 139*f3087befSAndrew Turner TEST_INTERVAL (exp10, 1, OFlowBound, 50000) 140*f3087befSAndrew Turner TEST_INTERVAL (exp10, -1, UFlowBound, 50000) 141*f3087befSAndrew Turner TEST_INTERVAL (exp10, OFlowBound, inf, 5000) 142*f3087befSAndrew Turner TEST_INTERVAL (exp10, UFlowBound, -inf, 5000) 143*f3087befSAndrew Turner #endif 144