/*------------------------------------------------------------------------- expf.c - Computes e**x of a 32-bit float as outlined in [1] Copyright (C) 2001, 2002, Jesus Calvino-Fraga, jesusc@ieee.org This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, if you link this library with other files, some of which are compiled with SDCC, to produce an executable, this library does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License. -------------------------------------------------------------------------*/ /* [1] William James Cody and W. M. Waite. _Software manual for the elementary functions_, Englewood Cliffs, N.J.:Prentice-Hall, 1980. */ /* Version 1.0 - Initial release */ #define __SDCC_MATH_LIB #include #include #include #ifdef MATH_ASM_MCS51 #define __SDCC_FLOAT_LIB #include // TODO: share with other temps static __bit sign_bit; static __data unsigned char expf_y[4]; static __data unsigned char n; float expf(float x) { x; __asm mov c, acc.7 mov _sign_bit, c // remember sign clr acc.7 // and make input positive mov r0, a mov c, b.7 rlc a // extract exponent add a, #153 jc expf_not_zero // input is a very small number, so e^x is 1.000000 mov dptr, #0 mov b, #0x80 mov a, #0x3F ljmp expf_exit expf_not_zero: // TODO: check exponent for very small values, and return zero mov _n, #0 mov a, dpl add a, #0xE8 // is x >= 0.69314718 mov a, dph addc a, #0x8D mov a, b addc a, #0xCE mov a, r0 addc a, #0xC0 mov a, r0 jnc expf_no_range_reduction expf_range_reduction: mov (_expf_y + 0), dpl // keep copy of x in "exp_y" mov (_expf_y + 1), dph mov (_expf_y + 2), b mov (_expf_y + 3), a mov r0, #0x3B push ar0 mov r0, #0xAA push ar0 mov r0, #0xB8 push ar0 mov r0, #0x3F push ar0 lcall ___fsmul // x * 1.442695041 = x / ln(2) dec sp dec sp dec sp dec sp lcall ___fs2uchar // n = int(x * 1.442695041) mov a, dpl mov _n, a add a, #128 jnc expf_range_ok lcall fs_return_inf // exponent overflow ljmp expf_exit expf_range_ok: mov r0,#0x00 mov r1,#0x80 mov r2,#0x31 mov r3,#0xBF lcall expf_scale_and_add mov (_expf_y + 0), dpl mov (_expf_y + 1), dph mov (_expf_y + 2), b mov (_expf_y + 3), a mov r0,#0x83 mov r1,#0x80 mov r2,#0x5E mov r3,#0x39 lcall expf_scale_and_add expf_no_range_reduction: // Compute e^x using the cordic algorithm. This works over an // input range of 0 to 0.69314712. Can be extended to work from // 0 to 1.0 if the results are normalized, but over the range // we use, the result is always from 1.0 to 1.99999988 (fixed // exponent of 127) expf_cordic_begin: mov c, b.7 rlc a // extract exponent to acc setb b.7 mov r1, dpl // mantissa to r4/r3/r2/r1 mov r2, dph mov r3, b mov r4, #0 // first, align the input into a 32 bit long cjne a, #121, exp_lshift sjmp exp_noshift exp_lshift: jc exp_rshift // exp_a is greater than 121, so left shift add a, #135 lcall fs_lshift_a sjmp exp_noshift exp_rshift: // exp_a is less than 121, so right shift cpl a add a, #122 lcall fs_rshift_a exp_noshift: // r4/r3/r2/r1 = x clr a mov (_expf_y + 0), a // y = 1.0; mov (_expf_y + 1), a mov (_expf_y + 2), a mov (_expf_y + 3), #0x20 mov dptr, #__fs_natural_log_table mov r0, a // r0 = i exp_cordic_loop: clr a movc a, @a+dptr mov b, a inc dptr clr a movc a, @a+dptr // r7/r6/r5/b = table[i] mov r5, a inc dptr clr a movc a, @a+dptr mov r6, a inc dptr clr a movc a, @a+dptr mov r7, a inc dptr clr c mov a, r1 subb a, b // compare x to table[i] mov a, r2 subb a, r5 mov a, r3 subb a, r6 mov a, r4 subb a, r7 jc exp_cordic_skip // if table[i] < x clr c mov a, r1 subb a, b mov r1, a // x -= table[i] mov a, r2 subb a, r5 mov r2, a mov a, r3 subb a, r6 mov r3, a mov a, r4 subb a, r7 mov r4, a mov b, (_expf_y + 0) mov r5, (_expf_y + 1) // r7/r6/r5/b = y >> i mov r6, (_expf_y + 2) mov r7, (_expf_y + 3) mov a, r0 lcall __fs_cordic_rshift_r765_unsigned mov a, (_expf_y + 0) add a, b mov (_expf_y + 0), a mov a, (_expf_y + 1) addc a, r5 mov (_expf_y + 1), a // y += (y >> i) mov a, (_expf_y + 2) addc a, r6 mov (_expf_y + 2), a mov a, (_expf_y + 3) addc a, r7 mov (_expf_y + 3), a exp_cordic_skip: inc r0 cjne r0, #27, exp_cordic_loop mov r4, (_expf_y + 3) mov r3, (_expf_y + 2) mov r2, (_expf_y + 1) mov r1, (_expf_y + 0) mov exp_a, #121 lcall fs_normalize_a // end of cordic mov a, #127 add a, _n // ldexpf(x, n) mov exp_a, a lcall fs_round_and_return jnb _sign_bit, expf_done push dpl push dph push b push acc mov dptr, #0 mov b, #0x80 mov a, #0x3F lcall ___fsdiv // 1.0 / x dec sp dec sp dec sp dec sp expf_done: clr acc.7 // Result is always positive! expf_exit: __endasm; #pragma less_pedantic } static void dummy1(void) __naked { __asm .globl fs_lshift_a expf_scale_and_add: push ar0 push ar1 push ar2 push ar3 mov dpl, _n lcall ___uchar2fs // turn n into float lcall ___fsmul // n * scale_factor dec sp dec sp dec sp dec sp push dpl push dph push b push acc mov dpl, (_expf_y + 0) mov dph, (_expf_y + 1) mov b, (_expf_y + 2) mov a, (_expf_y + 3) lcall ___fsadd // x += (n * scale_factor) dec sp dec sp dec sp dec sp ret __endasm; } static void dummy(void) __naked { __asm .globl fs_lshift_a fs_lshift_a: jz fs_lshift_done push ar0 mov r0, a fs_lshift_loop: clr c mov a, r1 rlc a mov r1, a mov a, r2 rlc a mov r2, a mov a, r3 rlc a mov r3, a mov a, r4 rlc a mov r4, a djnz r0, fs_lshift_loop pop ar0 fs_lshift_done: ret __endasm; } #else // not MATH_ASM_MCS51 #define P0 0.2499999995E+0 #define P1 0.4160288626E-2 #define Q0 0.5000000000E+0 #define Q1 0.4998717877E-1 #define P(z) ((P1*z)+P0) #define Q(z) ((Q1*z)+Q0) #define C1 0.693359375 #define C2 -2.1219444005469058277e-4 #define BIGX 88.72283911 /* ln(HUGE_VALF) */ #define EXPEPS 1.0E-7 /* exp(1.0E-7)=0.0000001 */ #define K1 1.4426950409 /* 1/ln(2) */ float expf(float x) _FLOAT_FUNC_REENTRANT { int n; float xn, g, r, z, y; bool sign; if(x>=0.0) { y=x; sign=0; } else { y=-x; sign=1; } if(yBIGX) { if(sign) { errno=ERANGE; return HUGE_VALF ; } else { return 0.0; } } z=y*K1; n=z; if(n<0) --n; if(z-n>=0.5) ++n; xn=n; g=((y-xn*C1))-xn*C2; z=g*g; r=P(z)*g; r=0.5+(r/(Q(z)-r)); n++; z=ldexpf(r, n); if(sign) return 1.0/z; else return z; } #endif