xref: /freebsd/contrib/arm-optimized-routines/math/exp10.c (revision f3087bef11543b42e0d69b708f367097a4118d24)
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