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
special_case(uint64_t sbits,double_t tmp,uint64_t ki)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
exp10(double x)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