1

I wrote a short program in C to perform linear interpolation, which iterates to give a root of a function to a given number of decimal points. So far, for a function f(x):

    long double f(long double x) {
        return (pow(x, 3) + 2 * x - 8);
    }

The program fails to converge to a 1dp value. The program updates variables a and b, between which the root of f(x) lies, until a and b both round to the same number at a given precision. Using long doubles and the above function, a debugger shows for the first 2 iterations:

    a = 1.5555555555555556
    a = 1.6444444444444444

though it should have been:

    a = 1.5555555555555556
    a = 1.653104925053533

The program fails to update values after this. The equation for linear interpolation I'm using is a rearranged version of the mathematical one given here and the code I'm using is a C-version of a python program I wrote. Why does the C implementation get different values, despite the same algorithm, and how can I fix it?

OK I'm still getting the hang of this, but hopefully below I have a Minimal, Complete, and Verifiable example:

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

long double a; long double b; long double c; // The values for interpolation
long double fofa; long double fofb; long double fofc; // The values f(a), f(b) and f(c)
const int dp = 1; // The number of decimal places to be accurate to

long double f(long double x) {
    return (pow(x, 3) + 2 * x - 8);
}

int main(void) {
    a = 1; b = 2;
    while(roundf(a * pow(10, dp))/pow(10, dp) != roundf(b * pow(10, dp))/pow(10, dp)) { // While a and b don't round to the same number...

        fofa = f(a); fofb = f(b); // Resolve the functions
        printf("So f(a) = %g, f(b) = %g\n", (double)fofa, (double)fofb); // Print the results

        c = (b * abs(fofa) + a * abs(fofb)) / (abs(fofb) + abs(fofa)); // Linear Interpolation

        fofc = f(c);

        if(fofc < 0) {
            a = c;
        }
        else if(fofc == 0) {
            a = c;
            break;
        }
        else {
            b = c;
        }

    }

    printf("The root is %g, to %d decimal places, after %d iterations.\n", (double)a, dp, i);
}
AWSM
  • 78
  • 6
  • Can't you use some debugging to see what values you're getting for `fofa` (which is `f(a)`, I assume), `fofb`, and all the other intermediate values? – Teepeemm Sep 12 '16 at 22:00
  • What are you using as the value for `dp` (1, 10, 15, something else)? Why are you using `float`-precision with `roundf()` when you have `long double` for `a` and `fofa`, etc? (You should be using `roundl()`, and `powl()` (rather than `pow()`), unless you're using ``, but you should be mentioning that if you are and you wouldn't be using `roundf()` but just `round()`… – Jonathan Leffler Sep 12 '16 at 22:10
  • 1
    Please provide a [mcve], not a puzzle game. Also see [ask] – too honest for this site Sep 12 '16 at 22:11
  • What have decimal digits to do with the accuracy of a calculation done in binary? Should you be using an **epsilon**? I was tempted to link a wikipedia page , but [this SO question](http://stackoverflow.com/questions/2729637/does-epsilon-really-guarantees-anything-in-floating-point-computations) is more food for thought. – Weather Vane Sep 12 '16 at 22:12

2 Answers2

7

The function abs() (from <stdlib.h>) has the signature int abs(int); — you are getting integers from your calculations.

You should be using long double fabsl(long double); from <math.h>.

You should also be using powl() instead of pow() (long double vs double), and roundl() instead of roundf() (long double vs float).

Make sure you're using the correct types, in other words.


When you've fixed the type problems, you still have an issue with convergence. It would have helped if you had produced an MCVE (Minimal, Complete, Verifiable Example). However, this is an MCVE that I can deduce from your question:

#include <math.h>
#include <stdio.h>

static inline long double f(long double x)
{
    return(powl(x, 3) + 2 * x - 8);
}

int main(void)
{
    long double a = 1.0L;
    long double b = 2.0L;
    int dp = 6;

    while (roundl(a * powl(10, dp)) / powl(10, dp) != roundl(b * powl(10, dp)) / powl(10, dp))
    {
        long double fofa = f(a);
        long double fofb = f(b);
        long double c = (b * fabsl(fofa) + a * fabsl(fofb)) / (fabsl(fofb) + fabsl(fofa));
        long double fofc = f(c);
        printf("a = %+.10Lf, f(a) = %+.10Lf\n", a, fofa);
        printf("b = %+.10Lf, f(b) = %+.10Lf\n", b, fofb);
        printf("c = %+.10Lf, f(c) = %+.10Lf\n", c, fofc);
        putchar('\n');

        if (fofc < 0.0L)
        {
            a = c;
        }
        else if (fofc == 0.0L)
        {
            a = c;
            break;
        }
        else
        {
            b = c;
        }
    }
    printf("Result: a = %Lg\n", a);
    return 0;
}

The output I get from it is:

a = +1.0000000000, f(a) = -5.0000000000
b = +2.0000000000, f(b) = +4.0000000000
c = +1.5555555556, f(c) = -1.1248285322

a = +1.5555555556, f(a) = -1.1248285322
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6531049251, f(c) = -0.1762579238

a = +1.6531049251, f(a) = -0.1762579238
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6677455452, f(c) = -0.0258828049

a = +1.6677455452, f(a) = -0.0258828049
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6698816424, f(c) = -0.0037639074

a = +1.6698816424, f(a) = -0.0037639074
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6701919841, f(c) = -0.0005465735

a = +1.6701919841, f(a) = -0.0005465735
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6702370440, f(c) = -0.0000793539

a = +1.6702370440, f(a) = -0.0000793539
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6702435859, f(c) = -0.0000115206

a = +1.6702435859, f(a) = -0.0000115206
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6702445357, f(c) = -0.0000016726

a = +1.6702445357, f(a) = -0.0000016726
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6702446735, f(c) = -0.0000002428

a = +1.6702446735, f(a) = -0.0000002428
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6702446936, f(c) = -0.0000000353

a = +1.6702446936, f(a) = -0.0000000353
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6702446965, f(c) = -0.0000000051

a = +1.6702446965, f(a) = -0.0000000051
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6702446969, f(c) = -0.0000000007

a = +1.6702446969, f(a) = -0.0000000007
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6702446970, f(c) = -0.0000000001

a = +1.6702446970, f(a) = -0.0000000001
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6702446970, f(c) = -0.0000000000

a = +1.6702446970, f(a) = -0.0000000000
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6702446970, f(c) = -0.0000000000

a = +1.6702446970, f(a) = -0.0000000000
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6702446970, f(c) = -0.0000000000

a = +1.6702446970, f(a) = -0.0000000000
b = +2.0000000000, f(b) = +4.0000000000
c = +1.6702446970, f(c) = -0.0000000000

The reason for the infinite loop is clear; the difference between a and b is not a small fraction. You need to review your convergence criterion. It should probably be comparing fofc with 0.0 to within the given number of decimal places — or something along those lines.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thanks very much, surprisingly it does converge now (to too many dp, but I'll figure that one out!). – AWSM Sep 12 '16 at 23:17
  • Following the hints from [Lutzl](http://stackoverflow.com/users/3088138/lutzl)'s [answer](http://stackoverflow.com/a/39459981), you might find the Wikipedia article on the [False Position Method](https://en.wikipedia.org/wiki/False_position_method) enlightening. I've not attempted to fix the math further. – Jonathan Leffler Sep 13 '16 at 00:07
3

What you are implementing is called regula falsi or false position method.

It is actually not necessary to employ the absolute values as long as the opposite sign condition is maintained.

There is a very well known stalling problem with plain vanilla regula falsi in that at the moment the function is convex over the remaining interval, one end point will no further be moved towards the root. There are easy modifications to avoid that, for instance inserting bisection steps. Even more simple to implement but harder to understand is the Illinois modification. See Wikipedia article for regula falsi for details.

Or this question and answer: Regula-Falsi Algorithm?


Adapted from answer in link:

#include<math.h>
#include<stdio.h>

long double f(long double x) {
   return powl(x, 3) + 2 * x - 8;
}

int main(void) {
    const int dp = 18;
    long double eps=0.5*powl(10,-dp);
    int i=0;
    long double a=1, fofa = f(a);
    long double b=2, fofb = f(b);

    printf("\na=%.21Lf b=%.21Lf  fofa=%.21Lf  fofb=%.21Lf\n------\n",a,b, fofa,fofb);

    if(signbit(fofb)==signbit(fofa)) {
        printf("Warning, initial values do not have opposite sign!\n");
    }
    do {
        long double c=(a*fofb-b*fofa)/(fofb-fofa), fofc = f(c);

        if( signbit(fofc)!=signbit(fofa) ) {
            b=a; fofb=fofa;
            a=c; fofa=fofc;
        } else {
            a=c; fofa=fofc;
            fofb *= 0.5;
        }
        i++;  
        printf("\na=c=%.21Lf b=%.21Lf  fofa=fofc=%.21Lf  fofb=%.21Lf",c,b, fofc,fofb);

    } while(fabsl(b-a)>eps);

    printf("\ngoal reached after %d iterations\n",i);

    return 0;
}

with result

a=1.000000000000000000000 b=2.000000000000000000000  fofa=-5.000000000000000000000  fofb=4.000000000000000000000
------

a=c=1.555555555555555555507 b=2.000000000000000000000  fofa=fofc=-1.124828532235939643549  fofb=2.000000000000000000000
a=c=1.715539947322212467064 b=1.555555555555555555507  fofa=fofc=0.480046589479470829469  fofb=-1.124828532235939643549
a=c=1.667685780603345490963 b=1.715539947322212467064  fofa=fofc=-0.026500999000164700194  fofb=0.480046589479470829469
a=c=1.670189362207942139265 b=1.715539947322212467064  fofa=fofc=-0.000573759143326624515  fofb=0.240023294739735414734
a=c=1.670297511133477909220 b=1.670189362207942139265  fofa=fofc=0.000547652143260468627  fofb=-0.000573759143326624515
a=c=1.670244695550498326532 b=1.670297511133477909220  fofa=fofc=-0.000000014643676336194  fofb=0.000547652143260468627
a=c=1.670244696962696986627 b=1.670297511133477909220  fofa=fofc=-0.000000000000373724489  fofb=0.000273826071630234313
a=c=1.670244696962769068724 b=1.670244696962696986627  fofa=fofc=0.000000000000373706274  fofb=-0.000000000000373724489
a=c=1.670244696962733028543 b=1.670244696962769068724  fofa=fofc=-0.000000000000000000434  fofb=0.000000000000373706274
a=c=1.670244696962733028651 b=1.670244696962733028543  fofa=fofc=0.000000000000000000867  fofb=-0.000000000000000000434
goal reached after 10 iterations
Community
  • 1
  • 1
Lutz Lehmann
  • 25,219
  • 2
  • 22
  • 51