/* * find log(N) * * if places < 360 * solve with cubically convergent algorithm above * * else * * let 'X' be 'close' to the solution (we use ~110 decimal places) * * let Y = N * exp(-X) - 1 * * then * * log(N) = X + log(1 + Y) * * since 'Y' will be small, we can use the efficient log_near_1 algorithm. * */ void M_log_basic_iteration(M_APM rr, int places, M_APM nn) { M_APM tmp0, tmp1, tmp2, tmpX; if (places < 360) { M_log_solve_cubic(rr, places, nn); } else { tmp0 = M_get_stack_var(); tmp1 = M_get_stack_var(); tmp2 = M_get_stack_var(); tmpX = M_get_stack_var(); M_log_solve_cubic(tmpX, 110, nn); m_apm_negate(tmp0, tmpX); m_apm_exp(tmp1, (places + 8), tmp0); m_apm_multiply(tmp2, tmp1, nn); m_apm_subtract(tmp1, tmp2, MM_One); M_log_near_1(tmp0, (places - 104), tmp1); m_apm_add(tmp1, tmpX, tmp0); m_apm_round(rr, places, tmp1); M_restore_stack(4); } }
void m_apm_log(M_APM r, int places, M_APM a) { M_APM tmp0, tmp1, tmp2; int mexp, dplaces; if (a->m_apm_sign <= 0) { M_apm_log_error_msg(M_APM_RETURN, "Warning! ... \'m_apm_log\', Negative argument"); M_set_to_zero(r); return; } tmp0 = M_get_stack_var(); tmp1 = M_get_stack_var(); tmp2 = M_get_stack_var(); dplaces = places + 8; /* * if the input is real close to 1, use the series expansion * to compute the log. * * 0.9999 < a < 1.0001 */ m_apm_subtract(tmp0, a, MM_One); if (tmp0->m_apm_sign == 0) /* is input exactly 1 ?? */ { /* if so, result is 0 */ M_set_to_zero(r); M_restore_stack(3); return; } if (tmp0->m_apm_exponent <= -4) { M_log_near_1(r, places, tmp0); M_restore_stack(3); return; } /* make sure our log(10) is accurate enough for this calculation */ /* (and log(2) which is called from M_log_basic_iteration) */ M_check_log_places(dplaces + 25); mexp = a->m_apm_exponent; if (mexp >= -4 && mexp <= 4) { M_log_basic_iteration(r, places, a); } else { /* * use log (x * y) = log(x) + log(y) * * here we use y = exponent of our base 10 number. * * let 'C' = log(10) = 2.3025850929940.... * * then log(x * y) = log(x) + ( C * base_10_exponent ) */ m_apm_copy(tmp2, a); mexp = tmp2->m_apm_exponent - 2; tmp2->m_apm_exponent = 2; /* force number between 10 & 100 */ M_log_basic_iteration(tmp0, dplaces, tmp2); m_apm_set_long(tmp1, (long)mexp); m_apm_multiply(tmp2, tmp1, MM_lc_log10); m_apm_add(tmp1, tmp2, tmp0); m_apm_round(r, places, tmp1); } M_restore_stack(3); /* restore the 3 locals we used here */ }