0

I've searched for the equation which calculates the ln of a number x and found out that this equation is:

enter image description here

and I've written this code to implement it:

double ln = x-1 ;
    for(int i=2;i<=5;i++)
    {
        double tmp = 1 ;
        for(int j=1;j<=i;j++)
            tmp *= (x-1) ;
        if(i%2==0)
            ln -= (tmp/i) ;
        else
            ln += (tmp/i) ;
    }
    cout << "ln: " << setprecision(10) << ln << endl ;

but unfortunately I'm getting outputs completely different from output on my calculator especially for large numbers, can anyone tell me where is the problem ?

andand
  • 17,134
  • 11
  • 53
  • 79
Mohamed Ibrahim Elsayed
  • 2,734
  • 3
  • 23
  • 43
  • give actual examples of problematic inputs and outputs please. – President James K. Polk Nov 08 '14 at 19:01
  • 4
    For for large values of `x`, `tmp *= (x-1)` most likely overflows within a few iterations. In addition, your formula holds only for `x` between 0 and 2. – barak manos Nov 08 '14 at 19:28
  • 3
    The series only converges for 0 < x < 2 ! You can use log (2^n x) = n log 2 + log x, precompute log 2 and divide x by the relevant 2^n so that it gets close to one for the series to converge rapidly – Alexandre C. Nov 08 '14 at 19:39
  • 4
    I'm amazed, 4 answers and it took a comment to point out the convergence domain... I've always wondered why schools nowadays drop the math requirement for a CS degree. – Mysticial Nov 08 '14 at 19:40
  • Why are you starting the exponentiation over for each term? – Ben Voigt Nov 08 '14 at 19:50
  • @Mysticial: They don't drop the math requirement (at least not proper universities). Most CS graduates simply forget most of the math at some point, because their job doesn't require them to use it. There are a very few (lucky in my personal opinion) SW engineers who get to make use of math as part of their job. – barak manos Nov 08 '14 at 19:52
  • @barakmanos The school I went to (Northwestern University) has no math requirement for CS (though I went EE, so I took it anyway). And having been in the workforce for just over a year now, I admit that I haven't had to use *any* math for my job. – Mysticial Nov 08 '14 at 20:01
  • @Mysticial: Is that the one in Boston? So have they decided to leave the math to MIT or what? I was once (upon a time) almost convinced to take CS there. Ended up going back home and take it there instead. Now I'm starting to think it must have been a good choice... – barak manos Nov 08 '14 at 20:06

6 Answers6

1

The equation you link to is an infinite series as implied by the ellipsis following the main part of the equation and as indicated more explicitly by the previous formulation on the same page:

enter image description here

In your case, you are only computing the first four terms. Later terms will add small refinements to the result to come closer to the actual value, but ultimately to compute all infinite steps will require infinite time.

However, what you can do is approximate your response to something like:

double ln(double x) {
  // validate 0 < x < 2
  double threshold = 1e-5;  // set this to whatever threshold you want
  double base = x-1;        // Base of the numerator; exponent will be explicit
  int den = 1;              // Denominator of the nth term
  int sign = 1;             // Used to swap the sign of each term
  double term = base;       // First term
  double prev = 0;          // Previous sum
  double result = term;     // Kick it off

  while (fabs(prev - result) > threshold) {
      den++;
      sign *=- 1;
      term *= base;
      prev = result;
      result += sign * term / den;
  }

  return result;
}

Caution: I haven't actually tested this so it may need some tweaking.

What this does is compute each term until the absolute difference between two consecutive terms is less than some threshold you establish.

Now this is not a particularly efficient way to do this. It's better to work with the functions the language you're using (in this case C++) provides to compute the natural log (which another poster has, I believe already shown to you). But there may be some value in trying this for yourself to see how it works.

Also, as barak manos notes below, this Taylor series only converges on the range (0, 2), so you will need to validate the value of x lies in that range before trying to run actual computation.

andand
  • 17,134
  • 11
  • 53
  • 79
  • 1
    The formula holds only for `x` between 0 and 2. See http://en.wikipedia.org/wiki/Natural_logarithm#Derivative.2C_Taylor_series. – barak manos Nov 08 '14 at 19:36
  • @barakmanos Thanks... yes, you're correct. I updated the comment describing what should be validated before computing the series. – andand Nov 08 '14 at 19:41
0

I believe the natural log in C++ language is simply log

user2649644
  • 124
  • 1
  • 2
  • 12
  • 7
    While factually correct, I don't think this answers the question. – Blastfurnace Nov 08 '14 at 19:06
  • @gniourf_gniourf: Flagging this a NAA would be inappropriate. It is an attempt at an answer, however it misunderstood the question. – Ben Voigt Nov 08 '14 at 20:23
  • @gniourf_gniourf: That's the problem with having essential information *only* in the title. Answer-writers probably read the question body ten times, but the title only once. – Ben Voigt Nov 08 '14 at 20:28
  • @gniourf_gniourf: And if you didn't look at the title, answering that question at the end with "Use the built-in natural logarithm function, named `log()` not `ln()`" is not unreasonable. – Ben Voigt Nov 08 '14 at 20:36
  • @gniourf_gniourf: I think you meant the interval `(0, 2]`? 2 leads to the alternating harmonic series, 0 leads to the non-alternating (and therefore divergent) series. – Ben Voigt Nov 08 '14 at 20:37
  • When I answered, I didn't read the problem fully, and that's my fault. No need to continue debating on whether or not I was commenting or answering, since it was off target. In regards to what you just quoted, that was added after I answered, or commented. No need to rehash the point and continue rehashing it. – user2649644 Nov 08 '14 at 20:39
0

That formula won't work for large inputs, because it would require you to take in consideration the highest degree member, which you can't because they are infinity many. It will only work for small inputs, where only the first terms of your series are relevant.

You can find ways to do that here: http://en.wikipedia.or/wiki/Pollard%27s_rho_algorithm_for_logarithms

and here: http://www.netlib.org/cephes/qlibdoc.html#qlog

SlySherZ
  • 1,631
  • 1
  • 16
  • 25
  • Moreover, calculating it only for 5 terms for even small numbers is going to be way off. You'll need a lot more terms to get close. You really need to find a better formula (one which converges faster and uses small numbers, e.g. ln(10) means the 20th term is calculating 9^20/20 or 12157665459056928801/20! If you want to do the calculation using double-precision floating point numbers in C++ in a reasonable amount of time and for a reasonable range of input values. – CXJ Nov 08 '14 at 19:25
  • 1
    @CXJ: Many taylor series expansions have a factorial in the denominator, but for log the factorial cancelled one in the numerator... Anyway, the series diverges for `x > 2`, so calculating `pow(9,20)/20` is totally pointless. – Ben Voigt Nov 08 '14 at 20:34
  • @BenVoigt I bow to your superior knowledge of mathematics. The last time I heard the term "Taylor series" was waaay back in college, and I haven't done that kind of math as part of my daily life since. ;-) – CXJ Nov 10 '14 at 19:19
  • @CXJ: I probably wouldn't be doing that kind of math in daily life either, except right now daily life is preparing a doctoral dissertation in systems engineering. For some reason committee members care about little things like ensuring convergence. – Ben Voigt Nov 10 '14 at 19:45
0

It wouldn't hurt to use long and long double instead of int and double. This may get a little more accuracy on some larger values. Also, your series only extending 5 levels deep is also limiting your accuracy.

Using a series like this is basically an approximation of the logarithmic answer.

Grantly
  • 2,546
  • 2
  • 21
  • 31
0

This version should be somewhat faster:

double const scale = 1.5390959186233239e-16;
double const offset = -709.05401552996614;

double fast_ln(double x)
{
    uint64_t xbits;
    memcpy(&xbits, &x, 8);
    // if memcpy not allowed, use 
    //       for( i = 0; i < 8; ++i ) i[(char*)xbits] = i[(char*)x];
    return xbits * scale + offset;
}

The trick is that this uses a 64-bit integer * 64-bit floating-point multiply, which involves a conversion of the integer to floating-point. Said floating-point representation is similar to scientific notation and requires a logarithm to find the appropriate exponent... but it is done purely in hardware and is very fast.

However it is doing a linear approximation within each octave, which is not very accurate. Using a lookup table for those bits would be far better.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

This should work. You just needed the part where if x>=2 shrink x by half and add 0.6931. The reason for 0.6931 is that is ln(2). If you wanted to you could add if (x >= 1024) return myLN(x/1024) + 6.9315 where 6.9315 is ln(1024). This will add speed for big values of x. The for loop with 100 could be much less like 20. I believe to get exact result for an integer its 17.

double myLN(double x) {

    if (x >= 2) {
        return myLN(x/2.0) + 0.6931;
    }

    x = x-1;

    double total = 0.0;
    double xToTheIPower = x;

    for (unsigned i = 1; i < 100; i++) {

        if (i%2 == 1) {
            total += xToTheIPower / (i);
        } else {
            total -= xToTheIPower / (i);
        }

        xToTheIPower *= x;
    }

    return total;
}
Cameron Monks
  • 107
  • 1
  • 7