porl_
porl_

Reputation: 21

Simpson's Composite Rule giving too large values for when n is very large

Using Simpson's Composite Rule to calculate the integral from 2 to 1,000 of 1/ln(x), however when using a large n (usually around 500,000), I start to get results that vary from the value my calculator and other sources give me (176.5644). For example, when n = 10,000,000, it gives me a value of 184.1495. Wondering why this is, since as n gets larger, the accuracy is supposed to increase and not decrease.

#include <iostream>
#include <cmath>

// the function f(x)
float f(float x)
{
    return (float) 1 / std::log(x);
}




float my_simpson(float a, float b, long int n)
{
  if (n % 2 == 1) n += 1; // since n has to be even
  float area, h = (b-a)/n;


  float x, y, z;
  for (int i = 1; i <= n/2; i++)
  {
    x = a + (2*i - 2)*h;
    y = a + (2*i - 1)*h;
    z = a + 2*i*h;
    area += f(x) + 4*f(y) + f(z);
  }

  return area*h/3;
}



int main()
{
    std::cout.precision(20);
    int upperBound = 1'000;
    int subsplits = 1'000'000;

    float approx = my_simpson(2, upperBound, subsplits);

    std::cout << "Output: " << approx << std::endl;

    return 0;
}

Update: Switched from floats to doubles and works much better now! Thank you!

Upvotes: 0

Views: 68

Answers (1)

Bob__
Bob__

Reputation: 12799

Unlike a real (in mathematical sense) number, a float has a limited precision.

A typical IEEE 754 32-bit (single precision) floating-point number binary representation dedicates only 24 bits (one of which is implicit) to the mantissa and that translates in roughly less than 8 decimal significant digits (please take this as a gross semplification).

A double on the other end, has 53 significand bits, making it more accurate and (usually) the first choice for numerical computations, these days.

since as n gets larger, the accuracy is supposed to increase and not decrease.

Unfortunately, that's not how it works. There's a sweat spot, but after that the accumulation of rounding errors prevales and the results diverge from their expected values.

In OP's case, this calculation

area += f(x) + 4*f(y) + f(z);

introduces (and accumulates) rounding errors, due to the fact that area becomes much greater than f(x) + 4*f(y) + f(z) (e.g 224678.937 vs. 0.3606823). The bigger n is, the sooner this gets relevant, making the result diverging from the real one.

As mentioned in the comments, another issue (undefined behavior) is that area isn't initialized (to zero).

Upvotes: 1

Related Questions