I need a method to calculate ln(x) for small x, other than Taylor series method

Click For Summary

Discussion Overview

The discussion revolves around methods for calculating the natural logarithm, ln(x), particularly for small values of x. Participants explore numerical approaches, including Newton's method and Taylor series expansion, while seeking alternative methods for small x values.

Discussion Character

  • Exploratory
  • Technical explanation
  • Debate/contested
  • Mathematical reasoning

Main Points Raised

  • One participant describes a method using Newton's method for x ≥ 1 and Taylor series for 0 < x < 1, noting convergence issues with the Taylor series for small x.
  • Another suggests evaluating the integral of 1/x from x to 1 as an alternative method.
  • Concerns are raised about the performance of Newton's method for x < 1, prompting questions about its implementation.
  • A code snippet is shared, illustrating the implementation of both methods, but a participant points out a potential error in the Taylor series calculation.
  • Participants discuss the implications of using ln(x) = -ln(1/x) for x < 1, with one noting that this approach may lead to computational difficulties due to the need to calculate exp(1000000) for very small x values.
  • Another participant mentions the possibility of using the property ln(a·10^n) = ln(a) + n·ln(10) to work around limitations in calculating ln for small arguments.
  • One participant expresses confidence in finding ln(0.000001) using a specific algorithm, suggesting that it simplifies to a manageable form.

Areas of Agreement / Disagreement

There is no consensus on a single method for calculating ln(x) for small x values. Multiple competing views and methods are presented, with participants expressing differing opinions on the effectiveness and feasibility of the approaches discussed.

Contextual Notes

Participants acknowledge limitations in computing resources and the convergence of series, particularly for small x values. The discussion highlights the complexity of numerical methods and the need for careful consideration of algorithmic choices.

hkBattousai
Messages
64
Reaction score
0
I'm aiming to calculate ln(x) numerically. I'm using the following procedure for this:
1) If x is greater or equal to 1, use Newton's method.
2) If x is smaller between 0 and 1, use Taylor series expansion.

Newton's method works good, but I have problems with Taylor series expansion method.
\ln(1+x)=\sum_{n=1}^\infty \frac{(-1)^{n+1}}{n} x^n = x - \frac{x^2}{2} + \frac{x^3}{3} - \cdots \quad{\rm for}\quad \left|x\right| \leq 1\quad
Sum of this series is \small -\infty\normalsize for \small x=-1\normalsize, but when you try to take sum of it using a computer, it doesn't converge because of computing limitations (like using truncated series). I can't either use Newton's method for this interval, because it has a bad performance for small x values (neither it converges for small x).

For x=0.000001;
ln(x) = -13.8155... (actual)
ln(x) = -1.71828... (calculated)
(The summation goes on until absolute value of any element of the series is below a user defined \epsilon value, which is equal to 1.0 \times 10^{-14} for this particular example.)

I need a third method to calculate ln(x) for small x argument.
Can you please suggest me one?
 
Last edited:
Physics news on Phys.org
Maybe you could try directly evaluating the integral:

\int^{x}_{1}\frac{1}{x}dx
 
hkBattousai said:
I'm aiming to calculate ln(x) numerically. I'm using the following procedure for this:
1) If x is greater or equal to 1, use Newton's method.
2) If x is smaller between 0 and 1, use Taylor series expansion.

What's wrong with Newton's method if x < 1? How are you doing it?
 
hotvette said:
What's wrong with Newton's method if x < 1? How are you doing it?

Code:
// Public method
long double Math::ln(long double num) throw(MathInvalidArgument)
{
	if (num < 0)
	{
		throw(MathInvalidArgument(L"You tried to take ln() of a negative number."));
	}
	else if (num == 0.0)
	{
		long double zero = 0.0;
		return -1.0 / zero;	// return minus infinity
	}
	else if (num <= 1.0)
	{
		return ln_ByTaylorSeriesExpansion(num);
	}
	else
	{
		return ln_ByNewtonsMethod(num);
	}
}

// Private method
long double Math::ln_ByTaylorSeriesExpansion(long double arg)
{
	arg -= 1.0; // We will use series expansion for ln(x+1)
	long double Result = 0.0, NextSum;
	long double power_of_arg_over_i = 1.0;
	for (uint64_t i=1; true; i++)
	{
		power_of_arg_over_i *= arg / static_cast<long double>(i);
		NextSum = -static_cast<long double>(MinusOneToThePowerOf(i)) * power_of_arg_over_i;
		Result += NextSum;
		if (abs(NextSum) < m_PRECISION_EPSILON)
		{
			break;
		}
	}
	return Result;
}

// Private method
long double Math::ln_ByNewtonsMethod(long double arg)
{
	long double x_k = 1.0, x_k1,asd;
	for (uint64_t i=0; true; i++)
	{
		x_k1 = x_k - ((exp(x_k) - arg) / exp(x_k));
		asd = abs(x_k1 - x_k);	// This value does not approach to zero, fluctuating between 10^(-5) and 10^(-6)
		if (abs(x_k1 - x_k) < m_PRECISION_EPSILON) break;	// m_PRECISION_EPSILON is 10^(-14)
		x_k = x_k1;
	}
	return x_k1;
}
 
Last edited:
power_of_arg_over_i *= arg / static_cast<long double>(i);
Looking over this quickly, it seems like this isn't what you want. It looks like you're going to end up with a factorial on the bottom since the terms look like they would go

x,\ \frac{x^2}{2},\ \frac{x^3}{2*3},\ \frac{x^4}{2*3*4} ...
 
Actually, I'm very sure that's the mistake you made since you got
For x=0.000001;
ln(x) = -13.8155... (actual)
ln(x) = -1.71828... (calculated)

If you were calculating what I thought you were calculating, it's the Taylor series for e^{-x}-1. So you would get, after shifting the argument:
e^{-(0.000001-1)} - 1\approx e-1\approx 1.7182818...
 
Last edited:
LeonhardEuler said:
Actually, I'm very sure that's the mistake you made since you got


If you were calculating what I thought you were calculating, it's the Taylor series for e^{x}-1. So you would get:
e^{0.000001} - 1\approx e-1\approx 1.7182818...

Yeah, thanks, there was an error in the code... I feel embarrassed...

The denominator term was was also accumulating into multiplication, so it was becoming factorial of the current for loop variable. It was calculating exp() instead of ln(), what an exciting coincidence...
 
My new code is:

Code:
long double Math::ln(long double num) throw(MathInvalidArgument)
{
	if (num < 0)
	{
		throw(MathInvalidArgument(L"You tried to take ln() of a negative number."));
	}
	else if (num == 0.0)
	{
		long double zero = 0.0;
		return -1.0 / zero;	// return minus infinity
	}
	else if (num <= 1.0)
	{
		return ln_ByTaylorSeriesExpansion(num);
	}
	else
	{
		return ln_ByNewtonsMethod(num);
	}
}

long double Math::ln_ByTaylorSeriesExpansion(long double arg)
{
	arg -= 1.0; // We will use series expansion for ln(x+1)
	long double Result = 0.0, NextSum;
	long double power_of_arg = 1.0, power_of_arg_over_i;
	for (uint64_t i=1; true; i++)
	{
		power_of_arg *= arg;
		power_of_arg_over_i = power_of_arg / static_cast<long double>(i);
		NextSum = -static_cast<long double>(MinusOneToThePowerOf(i)) * power_of_arg_over_i;
		Result += NextSum;
		if (abs(NextSum) < m_PRECISION_EPSILON)
		{
			break;
		}
	}
	return Result;
}

long double Math::ln_ByNewtonsMethod(long double arg)
{
	long double x_k = 1.0, x_k1;
	for (uint64_t i=0; true; i++)
	{
		x_k1 = x_k - ((exp(x_k) - arg) / exp(x_k));
		if (abs(x_k1 - x_k) < m_PRECISION_EPSILON) break;
		x_k = x_k1;
	}
	return x_k1;
}
 
When 0 < x < 1, you could use
ln(x) = -ln(1/x)
 
  • #10
AlephZero said:
When 0 < x < 1, you could use
ln(x) = -ln(1/x)

Very good idea!
This way I can always use Newton's method, it is better since it converges super fast.
Thanks a lot!
 
  • #11
AlephZero said:
When 0 < x < 1, you could use
ln(x) = -ln(1/x)

Hmm, good advice but numerically not possible to implement.

For example, suppose that you want to calculate
ln(0.000001)
You send it into Newton's algorithm in this form
-ln(1000000)
I need to calculate
exp(1000000)
inside the Newton's algorithm, but obviously it is not possible because of the hardware and software limitations.
And I also realized that, with this code, there is a very low limit for the argument of ln(). I need to work around this problem.
 
  • #12
hkBattousai said:
Hmm, good advice but numerically not possible to implement.

For example, suppose that you want to calculate
ln(0.000001)
You send it into Newton's algorithm in this form
-ln(1000000)
I need to calculate
exp(1000000)
inside the Newton's algorithm, but obviously it is not possible because of the hardware and software limitations.
And I also realized that, with this code, there is a very low limit for the argument of ln(). I need to work around this problem.

You could work around it using the property that:
\ln{(a\cdot 10^{n})}=\ln{a}+n\ln{10}
 
  • #13
Hmmm, I must be missing something. I had no problem at all finding ln(0.000001) using hkBattousai's algorithm for Newton's method. It reduces to xk+1 = xk - 1 + arg/exp(xk).
 
Last edited:

Similar threads

  • · Replies 3 ·
Replies
3
Views
2K
  • · Replies 2 ·
Replies
2
Views
2K
  • · Replies 3 ·
Replies
3
Views
4K
  • · Replies 5 ·
Replies
5
Views
3K
  • · Replies 3 ·
Replies
3
Views
2K
  • · Replies 9 ·
Replies
9
Views
2K
  • · Replies 1 ·
Replies
1
Views
3K
  • · Replies 16 ·
Replies
16
Views
5K
  • · Replies 3 ·
Replies
3
Views
3K
Replies
1
Views
2K