/*------------------------------------------------------------------------- logf.c - Computes the natural log 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 #ifdef MATH_ASM_MCS51 #define __SDCC_FLOAT_LIB #include // TODO: share with other temps static __data unsigned char logf_tmp[4]; float logf(float x) { x; __asm // extract the two input, placing it into: // sign exponent mantiassa // ---- -------- --------- // x: sign_a exp_a r4/r3/r2 lcall fsgetarg logf_neg_check: jnb sign_a, logf_zero_check // TODO: set errno to EDOM (negative numbers not allowed) lcall fs_return_nan ljmp logf_exit logf_zero_check: cjne r4, #0, logf_ok // TODO: set errno to ERANGE (zero not allowed) setb sign_a lcall fs_return_inf ljmp logf_exit logf_ok: push exp_a mov a, #3 mov r1, #0 lcall fs_rshift_a clr a mov (_logf_tmp + 0), a // y = 0 mov (_logf_tmp + 1), a mov (_logf_tmp + 2), a mov (_logf_tmp + 3), a mov dptr, #__fs_natural_log_table mov r0, a logf_cordic_loop: mov ar7, r4 // r7/r6/r5 = x >> i mov ar6, r3 mov ar5, r2 mov b, r1 mov a, r0 lcall __fs_cordic_rshift_r765_unsigned mov a, r1 // check if x + (x >> i) > 1.0 add a, b mov a, r2 addc a, r5 mov a, r3 addc a, r6 mov a, r4 addc a, r7 anl a, #0xE0 jnz logf_cordic_skip mov a, r1 // x = x + (x >> i) add a, b mov r1, a mov a, r2 addc a, r5 mov r2, a mov a, r3 addc a, r6 mov r3, a mov a, r4 addc a, r7 mov r4, a clr a // y = y + log_table[i] movc a, @a+dptr add a, (_logf_tmp + 0) mov (_logf_tmp + 0), a mov a, #1 movc a, @a+dptr addc a, (_logf_tmp + 1) mov (_logf_tmp + 1), a mov a, #2 movc a, @a+dptr addc a, (_logf_tmp + 2) mov (_logf_tmp + 2), a mov a, #3 movc a, @a+dptr addc a, (_logf_tmp + 3) mov (_logf_tmp + 3), a logf_cordic_skip: inc dptr inc dptr inc dptr inc dptr inc r0 cjne r0, #30, logf_cordic_loop // at this point, _logf_tmp has the natural log of the positive // normalized fractional part (0.5 to 1.0 -> 0.693 to 0.0) mov r4, (_logf_tmp + 3) mov r3, (_logf_tmp + 2) mov r2, (_logf_tmp + 1) mov r1, (_logf_tmp + 0) mov exp_a, #129 setb sign_a lcall fs_normalize_a pop acc cjne a, #126, logf_exponent // if the input exponent was 126, then we don't need to add // anything and we can just return the with we have already // TODO: which of these gives best accuracy??? lcall fs_zerocheck_return //lcall fs_round_and_return sjmp logf_exit logf_exponent: jc logf_exp_neg // the input exponent was greater than 126 logf_exp_pos: add a, #130 clr sign_b sjmp logf_exp_scale logf_exp_neg: // the input exponent was less than 126 cpl a add a, #127 setb sign_b logf_exp_scale: // r0 has abs(exp - 126) mov r0, a // put the log of faction into b, so we can use a to compute // the offset to be added to it or subtracted from it lcall fs_swap_a_b // multiply r0 by log(2), or r0 * 0xB17218 mov a, #0x18 mov b, r0 mul ab mov r1, a mov r2, b mov a, #0xB1 mov b, r0 mul ab mov r3, a mov r4, b mov a, #0x72 mov b, r0 mul ab add a, r2 mov r2, a mov a, b addc a, r3 mov r3, a clr a addc a, r4 mov r4, a // turn r0 * log(2) into a proper float mov exp_a, #134 lcall fs_normalize_a // now just add log(fractional) +/- log(2) * abs(exp - 126) lcall fsadd_direct_entry logf_exit: __endasm; #pragma less_pedantic } #else // not MATH_ASM_MCS51 /*Constants for 24 bits or less (8 decimal digits)*/ #define A0 -0.5527074855E+0 #define B0 -0.6632718214E+1 #define A(w) (A0) #define B(w) (w+B0) #define C0 0.70710678118654752440 #define C1 0.693359375 /*355.0/512.0*/ #define C2 -2.121944400546905827679E-4 float logf(float x) _FLOAT_FUNC_REENTRANT { #if defined(__SDCC_mcs51) && defined(__SDCC_MODEL_SMALL) \ && !defined(__SDCC_NOOVERLAY) volatile #endif float Rz; float f, z, w, znum, zden, xn; int n; if (x<=0.0) { errno=EDOM; return 0.0; } f=frexpf(x, &n); znum=f-0.5; if (f>C0) { znum-=0.5; zden=(f*0.5)+0.5; } else { n--; zden=znum*0.5+0.5; } z=znum/zden; w=z*z; Rz=z+z*(w*A(w)/B(w)); xn=n; return ((xn*C2+Rz)+xn*C1); } #endif