18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------+ 38c2ecf20Sopenharmony_ci | poly_tan.c | 48c2ecf20Sopenharmony_ci | | 58c2ecf20Sopenharmony_ci | Compute the tan of a FPU_REG, using a polynomial approximation. | 68c2ecf20Sopenharmony_ci | | 78c2ecf20Sopenharmony_ci | Copyright (C) 1992,1993,1994,1997,1999 | 88c2ecf20Sopenharmony_ci | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | 98c2ecf20Sopenharmony_ci | Australia. E-mail billm@melbpc.org.au | 108c2ecf20Sopenharmony_ci | | 118c2ecf20Sopenharmony_ci | | 128c2ecf20Sopenharmony_ci +---------------------------------------------------------------------------*/ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "exception.h" 158c2ecf20Sopenharmony_ci#include "reg_constant.h" 168c2ecf20Sopenharmony_ci#include "fpu_emu.h" 178c2ecf20Sopenharmony_ci#include "fpu_system.h" 188c2ecf20Sopenharmony_ci#include "control_w.h" 198c2ecf20Sopenharmony_ci#include "poly.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define HiPOWERop 3 /* odd poly, positive terms */ 228c2ecf20Sopenharmony_cistatic const unsigned long long oddplterm[HiPOWERop] = { 238c2ecf20Sopenharmony_ci 0x0000000000000000LL, 248c2ecf20Sopenharmony_ci 0x0051a1cf08fca228LL, 258c2ecf20Sopenharmony_ci 0x0000000071284ff7LL 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define HiPOWERon 2 /* odd poly, negative terms */ 298c2ecf20Sopenharmony_cistatic const unsigned long long oddnegterm[HiPOWERon] = { 308c2ecf20Sopenharmony_ci 0x1291a9a184244e80LL, 318c2ecf20Sopenharmony_ci 0x0000583245819c21LL 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define HiPOWERep 2 /* even poly, positive terms */ 358c2ecf20Sopenharmony_cistatic const unsigned long long evenplterm[HiPOWERep] = { 368c2ecf20Sopenharmony_ci 0x0e848884b539e888LL, 378c2ecf20Sopenharmony_ci 0x00003c7f18b887daLL 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define HiPOWERen 2 /* even poly, negative terms */ 418c2ecf20Sopenharmony_cistatic const unsigned long long evennegterm[HiPOWERen] = { 428c2ecf20Sopenharmony_ci 0xf1f0200fd51569ccLL, 438c2ecf20Sopenharmony_ci 0x003afb46105c4432LL 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic const unsigned long long twothirds = 0xaaaaaaaaaaaaaaabLL; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/*--- poly_tan() ------------------------------------------------------------+ 498c2ecf20Sopenharmony_ci | | 508c2ecf20Sopenharmony_ci +---------------------------------------------------------------------------*/ 518c2ecf20Sopenharmony_civoid poly_tan(FPU_REG *st0_ptr) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci long int exponent; 548c2ecf20Sopenharmony_ci int invert; 558c2ecf20Sopenharmony_ci Xsig argSq, argSqSq, accumulatoro, accumulatore, accum, 568c2ecf20Sopenharmony_ci argSignif, fix_up; 578c2ecf20Sopenharmony_ci unsigned long adj; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci exponent = exponent(st0_ptr); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#ifdef PARANOID 628c2ecf20Sopenharmony_ci if (signnegative(st0_ptr)) { /* Can't hack a number < 0.0 */ 638c2ecf20Sopenharmony_ci arith_invalid(0); 648c2ecf20Sopenharmony_ci return; 658c2ecf20Sopenharmony_ci } /* Need a positive number */ 668c2ecf20Sopenharmony_ci#endif /* PARANOID */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* Split the problem into two domains, smaller and larger than pi/4 */ 698c2ecf20Sopenharmony_ci if ((exponent == 0) 708c2ecf20Sopenharmony_ci || ((exponent == -1) && (st0_ptr->sigh > 0xc90fdaa2))) { 718c2ecf20Sopenharmony_ci /* The argument is greater than (approx) pi/4 */ 728c2ecf20Sopenharmony_ci invert = 1; 738c2ecf20Sopenharmony_ci accum.lsw = 0; 748c2ecf20Sopenharmony_ci XSIG_LL(accum) = significand(st0_ptr); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (exponent == 0) { 778c2ecf20Sopenharmony_ci /* The argument is >= 1.0 */ 788c2ecf20Sopenharmony_ci /* Put the binary point at the left. */ 798c2ecf20Sopenharmony_ci XSIG_LL(accum) <<= 1; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ 828c2ecf20Sopenharmony_ci XSIG_LL(accum) = 0x921fb54442d18469LL - XSIG_LL(accum); 838c2ecf20Sopenharmony_ci /* This is a special case which arises due to rounding. */ 848c2ecf20Sopenharmony_ci if (XSIG_LL(accum) == 0xffffffffffffffffLL) { 858c2ecf20Sopenharmony_ci FPU_settag0(TAG_Valid); 868c2ecf20Sopenharmony_ci significand(st0_ptr) = 0x8a51e04daabda360LL; 878c2ecf20Sopenharmony_ci setexponent16(st0_ptr, 888c2ecf20Sopenharmony_ci (0x41 + EXTENDED_Ebias) | SIGN_Negative); 898c2ecf20Sopenharmony_ci return; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci argSignif.lsw = accum.lsw; 938c2ecf20Sopenharmony_ci XSIG_LL(argSignif) = XSIG_LL(accum); 948c2ecf20Sopenharmony_ci exponent = -1 + norm_Xsig(&argSignif); 958c2ecf20Sopenharmony_ci } else { 968c2ecf20Sopenharmony_ci invert = 0; 978c2ecf20Sopenharmony_ci argSignif.lsw = 0; 988c2ecf20Sopenharmony_ci XSIG_LL(accum) = XSIG_LL(argSignif) = significand(st0_ptr); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (exponent < -1) { 1018c2ecf20Sopenharmony_ci /* shift the argument right by the required places */ 1028c2ecf20Sopenharmony_ci if (FPU_shrx(&XSIG_LL(accum), -1 - exponent) >= 1038c2ecf20Sopenharmony_ci 0x80000000U) 1048c2ecf20Sopenharmony_ci XSIG_LL(accum)++; /* round up */ 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci XSIG_LL(argSq) = XSIG_LL(accum); 1098c2ecf20Sopenharmony_ci argSq.lsw = accum.lsw; 1108c2ecf20Sopenharmony_ci mul_Xsig_Xsig(&argSq, &argSq); 1118c2ecf20Sopenharmony_ci XSIG_LL(argSqSq) = XSIG_LL(argSq); 1128c2ecf20Sopenharmony_ci argSqSq.lsw = argSq.lsw; 1138c2ecf20Sopenharmony_ci mul_Xsig_Xsig(&argSqSq, &argSqSq); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* Compute the negative terms for the numerator polynomial */ 1168c2ecf20Sopenharmony_ci accumulatoro.msw = accumulatoro.midw = accumulatoro.lsw = 0; 1178c2ecf20Sopenharmony_ci polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddnegterm, 1188c2ecf20Sopenharmony_ci HiPOWERon - 1); 1198c2ecf20Sopenharmony_ci mul_Xsig_Xsig(&accumulatoro, &argSq); 1208c2ecf20Sopenharmony_ci negate_Xsig(&accumulatoro); 1218c2ecf20Sopenharmony_ci /* Add the positive terms */ 1228c2ecf20Sopenharmony_ci polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddplterm, 1238c2ecf20Sopenharmony_ci HiPOWERop - 1); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Compute the positive terms for the denominator polynomial */ 1268c2ecf20Sopenharmony_ci accumulatore.msw = accumulatore.midw = accumulatore.lsw = 0; 1278c2ecf20Sopenharmony_ci polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evenplterm, 1288c2ecf20Sopenharmony_ci HiPOWERep - 1); 1298c2ecf20Sopenharmony_ci mul_Xsig_Xsig(&accumulatore, &argSq); 1308c2ecf20Sopenharmony_ci negate_Xsig(&accumulatore); 1318c2ecf20Sopenharmony_ci /* Add the negative terms */ 1328c2ecf20Sopenharmony_ci polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evennegterm, 1338c2ecf20Sopenharmony_ci HiPOWERen - 1); 1348c2ecf20Sopenharmony_ci /* Multiply by arg^2 */ 1358c2ecf20Sopenharmony_ci mul64_Xsig(&accumulatore, &XSIG_LL(argSignif)); 1368c2ecf20Sopenharmony_ci mul64_Xsig(&accumulatore, &XSIG_LL(argSignif)); 1378c2ecf20Sopenharmony_ci /* de-normalize and divide by 2 */ 1388c2ecf20Sopenharmony_ci shr_Xsig(&accumulatore, -2 * (1 + exponent) + 1); 1398c2ecf20Sopenharmony_ci negate_Xsig(&accumulatore); /* This does 1 - accumulator */ 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Now find the ratio. */ 1428c2ecf20Sopenharmony_ci if (accumulatore.msw == 0) { 1438c2ecf20Sopenharmony_ci /* accumulatoro must contain 1.0 here, (actually, 0) but it 1448c2ecf20Sopenharmony_ci really doesn't matter what value we use because it will 1458c2ecf20Sopenharmony_ci have negligible effect in later calculations 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci XSIG_LL(accum) = 0x8000000000000000LL; 1488c2ecf20Sopenharmony_ci accum.lsw = 0; 1498c2ecf20Sopenharmony_ci } else { 1508c2ecf20Sopenharmony_ci div_Xsig(&accumulatoro, &accumulatore, &accum); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Multiply by 1/3 * arg^3 */ 1548c2ecf20Sopenharmony_ci mul64_Xsig(&accum, &XSIG_LL(argSignif)); 1558c2ecf20Sopenharmony_ci mul64_Xsig(&accum, &XSIG_LL(argSignif)); 1568c2ecf20Sopenharmony_ci mul64_Xsig(&accum, &XSIG_LL(argSignif)); 1578c2ecf20Sopenharmony_ci mul64_Xsig(&accum, &twothirds); 1588c2ecf20Sopenharmony_ci shr_Xsig(&accum, -2 * (exponent + 1)); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* tan(arg) = arg + accum */ 1618c2ecf20Sopenharmony_ci add_two_Xsig(&accum, &argSignif, &exponent); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (invert) { 1648c2ecf20Sopenharmony_ci /* We now have the value of tan(pi_2 - arg) where pi_2 is an 1658c2ecf20Sopenharmony_ci approximation for pi/2 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci /* The next step is to fix the answer to compensate for the 1688c2ecf20Sopenharmony_ci error due to the approximation used for pi/2 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* This is (approx) delta, the error in our approx for pi/2 1728c2ecf20Sopenharmony_ci (see above). It has an exponent of -65 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci XSIG_LL(fix_up) = 0x898cc51701b839a2LL; 1758c2ecf20Sopenharmony_ci fix_up.lsw = 0; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (exponent == 0) 1788c2ecf20Sopenharmony_ci adj = 0xffffffff; /* We want approx 1.0 here, but 1798c2ecf20Sopenharmony_ci this is close enough. */ 1808c2ecf20Sopenharmony_ci else if (exponent > -30) { 1818c2ecf20Sopenharmony_ci adj = accum.msw >> -(exponent + 1); /* tan */ 1828c2ecf20Sopenharmony_ci adj = mul_32_32(adj, adj); /* tan^2 */ 1838c2ecf20Sopenharmony_ci } else 1848c2ecf20Sopenharmony_ci adj = 0; 1858c2ecf20Sopenharmony_ci adj = mul_32_32(0x898cc517, adj); /* delta * tan^2 */ 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci fix_up.msw += adj; 1888c2ecf20Sopenharmony_ci if (!(fix_up.msw & 0x80000000)) { /* did fix_up overflow ? */ 1898c2ecf20Sopenharmony_ci /* Yes, we need to add an msb */ 1908c2ecf20Sopenharmony_ci shr_Xsig(&fix_up, 1); 1918c2ecf20Sopenharmony_ci fix_up.msw |= 0x80000000; 1928c2ecf20Sopenharmony_ci shr_Xsig(&fix_up, 64 + exponent); 1938c2ecf20Sopenharmony_ci } else 1948c2ecf20Sopenharmony_ci shr_Xsig(&fix_up, 65 + exponent); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci add_two_Xsig(&accum, &fix_up, &exponent); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* accum now contains tan(pi/2 - arg). 1998c2ecf20Sopenharmony_ci Use tan(arg) = 1.0 / tan(pi/2 - arg) 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci accumulatoro.lsw = accumulatoro.midw = 0; 2028c2ecf20Sopenharmony_ci accumulatoro.msw = 0x80000000; 2038c2ecf20Sopenharmony_ci div_Xsig(&accumulatoro, &accum, &accum); 2048c2ecf20Sopenharmony_ci exponent = -exponent - 1; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* Transfer the result */ 2088c2ecf20Sopenharmony_ci round_Xsig(&accum); 2098c2ecf20Sopenharmony_ci FPU_settag0(TAG_Valid); 2108c2ecf20Sopenharmony_ci significand(st0_ptr) = XSIG_LL(accum); 2118c2ecf20Sopenharmony_ci setexponent16(st0_ptr, exponent + EXTENDED_Ebias); /* Result is positive. */ 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci} 214