Back
C
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using bisection.                    *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/03/09                                                        *
 ******************************************************************************/
/*  stdio.h provides the "printf" function, used for printing text.           */
#include <stdio.h>
/*  Floating-point absolute value function, fabs, provided here.              */
#include <math.h>
/*  Function pointer notation is a little confusing. Create a typedef for it  *
 *  so we do not need to explicitly use it later.                             */
typedef double (*function)(double);
/*  Computes the root of a function using the bisection method.               */
static double bisection_method(function f, double a, double b)
{
    /*  Tell the algorithm to stop after several iterations to avoid an       *
     *  infinite loop. Double precision numbers have 52 bits in the mantissa, *
     *  so if |b - a| ~= 1, after 52 iterations of bisection we will get as   *
     *  close as we can to the root. To allow for |b - a| to be larger, halt  *
     *  the algorithm after at most 64 steps.                                 */
    const unsigned int maximum_number_of_iterations = 64U;
    /*  Getting exact roots is hard using floating-point numbers. Allow a     *
     *  tolerance in our computation. This value is double precision epsilon. */
    const double epsilon = 2.220446049250313E-16;
    /*  Variable for keeping track of how many iterations we have performed.  */
    unsigned int iters;
    /*  The midpoint for the bisection method. This will update as we iterate.*/
    double midpoint;
    /*  We do not require a < b, nor do we require f(a) < f(b). We only need  *
     *  one of these to evaluate to a negative under f and one to evaluate to *
     *  positive. We will call the negative entry left and positive one right.*/
    double left, right;
    /*  Evaluate f at the two endpoints to determine which is positive and    *
     *  which is negative. We transform [a, b] to [left, right] by doing this.*/
    const double a_eval = f(a);
    const double b_eval = f(b);
    /*  Rare case, f(a) = 0. Return a, no bisection needed.                   */
    if (a_eval == 0.0)
        return a;
    /*  Similarly, if f(b) = 0, then we have already found the root. Return b.*/
    if (b_eval == 0.0)
        return b;
    /*  Compare the two evaluations and set left and right accordingly.       */
    if (a_eval < b_eval)
    {
        /*  If both evaluations are negative, or if both are positive, then   *
         *  the bisection method will not work. Return NaN.                   */
        if (b_eval < 0.0 || a_eval > 0.0)
            return (a - a) / (a - a);
        /*  Otherwise, since f(a) < f(b), set left = a and right = b.         */
        left = a;
        right = b;
    }
    /*  In this case the function starts positive and tends to a negative.    */
    else
    {
        /*  Same sanity check as before. We need one evaluation to be         *
         *  negative and one to be positive. Abort if both have the same sign.*/
        if (a_eval < 0.0 || b_eval > 0.0)
            return (a - a) / (a - a);
        /*  Since f(a) > f(b), set left = b and right = a.                    */
        left = b;
        right = a;
    }
    /*  Start the bisection method. Compute the midpoint of a and b.          */
    midpoint = 0.5 * (a + b);
    /*  Iteratively divide the range in half to find the root.                */
    for (iters = 0; iters < maximum_number_of_iterations; ++iters)
    {
        /*  If f(x) is very small, we are close to a root and can break out   *
         *  of this for loop. Check for this.                                 */
        const double eval = f(midpoint);
        if (fabs(eval) <= epsilon)
            break;
        /*  Apply bisection to get a better approximation for the root. We    *
         *  have f(left) < 0 < f(right). If f(midpoint) < 0, replace the      *
         *  interval [left, right] with [midpoint, right]. Set left to the    *
         *  midpoint and reset the midpoint to be closer to right.            */
        if (eval < 0.0)
        {
            left = midpoint;
            midpoint = 0.5 * (midpoint + right);
        }
        /*  In the other case, f(midpoint) > 0, we replace right with the     *
         *  midpoint, changing [left, right] into [left, midpoint]. We then   *
         *  set the midpoint to be closer to left.                            */
        else
        {
            right = midpoint;
            midpoint = 0.5 * (left + midpoint);
        }
    }
    /*  After n iterations, we are no more than |b - a| / 2^n away from the   *
     *  root of the function. 1 / 2^n goes to zero very quickly, meaning the  *
     *  convergence is very quick.                                            */
    return midpoint;
}
/*  End of bisection_method.                                                  */
/*  Main routine used for testing our implementation of the bisection method. */
int main(void)
{
    /*  pi is somewhere between 3 and 4, and it is a root to sine.            */
    const double a = 3.0;
    const double b = 4.0;
    /*  Compute pi using bisection. We should get pi = 3.14159..., accurate   *
     *  to about 16 decimals.                                                 */
    const double pi = bisection_method(sin, a, b);
    printf("pi = %.16f\n", pi);
    return 0;
}
 
C3
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using bisection.                    *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/05/27                                                        *
 ******************************************************************************/
/*  std::io provides the "printfn" function, used for printing text.          */
import std::io;
/*  Floating-point absolute value function, abs, provided here.               */
import std::math;
/*  Function pointer notation is a little confusing. Create a typedef for it  *
 *  so we do not need to explicitly use it later.                             */
alias RealFunc = fn double(double);
/*  Computes the root of a function using the bisection method.               */
fn double bisection_method(RealFunc f, double a, double b)
{
    /*  Tell the algorithm to stop after several iterations to avoid an       *
     *  infinite loop. Double precision numbers have 52 bits in the mantissa, *
     *  so if |b - a| ~= 1, after 52 iterations of bisection we will get as   *
     *  close as we can to the root. To allow for |b - a| to be larger, halt  *
     *  the algorithm after at most 64 steps.                                 */
    const uint MAXIMUM_NUMBER_OF_ITERATIONS = 64U;
    /*  Getting exact roots is hard using floating-point numbers. Allow a     *
     *  tolerance in our computation. This value is double precision epsilon. */
    const double EPSILON = 2.220446049250313E-16;
    /*  Variable for keeping track of how many iterations we have performed.  */
    uint iters;
    /*  The midpoint for the bisection method. This will update as we iterate.*/
    double midpoint;
    /*  We do not require a < b, nor do we require f(a) < f(b). We only need  *
     *  one of these to evaluate to a negative under f and one to evaluate to *
     *  positive. We will call the negative entry left and positive one right.*/
    double left, right;
    /*  Evaluate f at the two endpoints to determine which is positive and    *
     *  which is negative. We transform [a, b] to [left, right] by doing this.*/
    double a_eval = f(a);
    double b_eval = f(b);
    /*  Rare case, f(a) = 0. Return a, no bisection needed.                   */
    if (a_eval == 0.0) {
        return a;
    }
    /*  Similarly, if f(b) = 0, then we have already found the root. Return b.*/
    if (b_eval == 0.0) {
        return b;
    }
    /*  Compare the two evaluations and set left and right accordingly.       */
    if (a_eval < b_eval)
    {
        /*  If both evaluations are negative, or if both are positive, then   *
         *  the bisection method will not work. Return NaN.                   */
        if (b_eval < 0.0 || a_eval > 0.0) {
            return (a - a) / (a - a);
        }
        /*  Otherwise, since f(a) < f(b), set left = a and right = b.         */
        left = a;
        right = b;
    }
    /*  In this case the function starts positive and tends to a negative.    */
    else
    {
        /*  Same sanity check as before. We need one evaluation to be         *
         *  negative and one to be positive. Abort if both have the same sign.*/
        if (a_eval < 0.0 || b_eval > 0.0) {
            return (a - a) / (a - a);
        }
        /*  Since f(a) > f(b), set left = b and right = a.                    */
        left = b;
        right = a;
    }
    /*  Start the bisection method. Compute the midpoint of a and b.          */
    midpoint = 0.5 * (a + b);
    /*  Iteratively divide the range in half to find the root.                */
    for (iters = 0; iters < MAXIMUM_NUMBER_OF_ITERATIONS; ++iters)
    {
        /*  If f(x) is very small, we are close to a root and can break out   *
         *  of this for loop. Check for this.                                 */
        double eval = f(midpoint);
        if (math::abs(eval) <= EPSILON) {
            break;
        }
        /*  Apply bisection to get a better approximation for the root. We    *
         *  have f(left) < 0 < f(right). If f(midpoint) < 0, replace the      *
         *  interval [left, right] with [midpoint, right]. Set left to the    *
         *  midpoint and reset the midpoint to be closer to right.            */
        if (eval < 0.0)
        {
            left = midpoint;
            midpoint = 0.5 * (midpoint + right);
        }
        /*  In the other case, f(midpoint) > 0, we replace right with the     *
         *  midpoint, changing [left, right] into [left, midpoint]. We then   *
         *  set the midpoint to be closer to left.                            */
        else
        {
            right = midpoint;
            midpoint = 0.5 * (left + midpoint);
        }
    }
    /*  After n iterations, we are no more than |b - a| / 2^n away from the   *
     *  root of the function. 1 / 2^n goes to zero very quickly, meaning the  *
     *  convergence is very quick.                                            */
    return midpoint;
}
/*  End of bisection_method.                                                  */
/*  math::sin is a macro, which we cannot take the address of. C3 allows      *
 *  linking with the C standard library, including libm. Tell the compiler    *
 *  about the C routine so that we may call it from our main function.        */
extern fn double sin(double x) @extern("sin");
/*  Main routine used for testing our implementation of the bisection method. */
fn void main()
{
    /*  pi is somewhere between 3 and 4, and it is a root to sine.            */
    const double A = 3.0;
    const double B = 4.0;
    /*  Compute pi using bisection. We should get pi = 3.14159..., accurate   *
     *  to about 16 decimals.                                                 */
    double pi = bisection_method(&sin, A, B);
    io::printfn("pi = %.16f", pi);
}
 
C++
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using the bisection method.         *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/03/09                                                        *
 ******************************************************************************/
/*  stdio provides the "printf" function, used for printing text.             */
#include <cstdio>
/*  Floating-point absolute value function, fabs, provided here.              */
#include <cmath>
/*  Function pointer notation is a little confusing. Create a typedef for it  *
 *  so we do not need to explicitly use it later.                             */
typedef double (*function)(double);
/*  Class providing an implementation of the bisection method.                */
class Bisection {
    /*  The error after n iterations is |b - a| / 2^n. Since double has a     *
     *  52-bit mantissa, if |b - a| ~= 1, then after 52 steps we can halt the *
     *  program. To allow for |b - a| to be larger, we stop the process after *
     *  at most 64 iterations.                                                */
    static const unsigned int maximum_number_of_iterations = 64U;
    /*  We want the function visible outside the class. Declare it public.    */
    public:
        /*  Computes the root of a function using the bisection method.       */
        static double root(function f, double a, double b)
        {
            /*  The maximum allowed error. This is double precision epsilon.  */
            const double epsilon = 2.220446049250313E-16;
            /*  Variable for keeping track of the number of iterations.       */
            unsigned int iters;
            /*  The midpoint for the bisection. This updates as we iterate.   */
            double midpoint;
            /*  We do not require a < b, nor f(a) < f(b). We need one of      *
             *  these to evaluate negative under f and one to evaluate to     *
             *  positive. Call the negative entry left and positive one right.*/
            double left, right;
            /*  Evaluate f at the endpoints to determine which is positive    *
             *  and which is negative, transforming [a, b] to [left, right].  */
            const double a_eval = f(a);
            const double b_eval = f(b);
            /*  Rare case, f(a) = 0. Return a, no bisection needed.           */
            if (a_eval == 0.0)
                return a;
            /*  Similarly, if f(b) = 0, then we found the root. Return b.     */
            if (b_eval == 0.0)
                return b;
            /*  Compare the two evaluations and set the left and right values.*/
            if (a_eval < b_eval)
            {
                /*  If both evaluations are negative, or if both are positive,*
                 *  then the bisection method will not work. Return NaN.      */
                if (b_eval < 0.0 || a_eval > 0.0)
                    return (a - a) / (a - a);
                /*  Otherwise, since f(a) < f(b), set left = a and right = b. */
                left = a;
                right = b;
            }
            /*  In this case the function starts positive and goes negative.  */
            else
            {
                /*  Same sanity check as before. We need one evaluation to be *
                 *  negative and one to be positive. Abort if the signs agree.*/
                if (a_eval < 0.0 || b_eval > 0.0)
                    return (a - a) / (a - a);
                /*  Since f(a) > f(b), set left = b and right = a.            */
                left = b;
                right = a;
            }
            /*  Start the bisection method. Compute the midpoint of a and b.  */
            midpoint = 0.5 * (a + b);
            /*  Iteratively divide the range in half to find the root.        */
            for (iters = 0; iters < maximum_number_of_iterations; ++iters)
            {
                /*  If f(x) is very small, we are close to a root and can     *
                 *  break out of this for loop. Check for this.               */
                const double eval = f(midpoint);
                if (std::fabs(eval) <= epsilon)
                    break;
                /*  Apply bisection to get a better approximation. We have    *
                 *  f(left) < 0 < f(right). If f(midpoint) < 0, replace the   *
                 *  interval [left, right] with [midpoint, right]. Set left   *
                 *  to the midpoint and set midpoint to be closer to right.   */
                if (eval < 0.0)
                {
                    left = midpoint;
                    midpoint = 0.5 * (midpoint + right);
                }
                /*  If f(midpoint) > 0, then replace right with the midpoint, *
                 *  changing [left, right] into [left, midpoint]. We then set *
                 *  the midpoint to be closer to left.                        */
                else
                {
                    right = midpoint;
                    midpoint = 0.5 * (left + midpoint);
                }
            }
            /*  After n iterations, we are at most |b - a| / 2^n from the     *
             *  root of the function. 1 / 2^n goes to zero very quickly,      *
             *  meaning the convergence is very quick.                        */
            return midpoint;
        }
        /*  End of root.                                                      */
};
/*  End of Bisection definition.                                              */
/*  Main routine used for testing our implementation of the Bisection method. */
int main(void)
{
    /*  pi is somewhere between 3 and 4, and it is a root to sine.            */
    const double a = 3.0;
    const double b = 4.0;
    /*  Compute pi using bisection. We should get pi = 3.14159..., accurate   *
     *  to about 16 decimals.                                                 */
    const double pi = Bisection::root(std::sin, a, b);
    std::printf("pi = %.16f\n", pi);
    return 0;
}
 
C#
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using the bisection method.         *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/03/28                                                        *
 ******************************************************************************/
/*  Console.WriteLine and Math.Abs are both provided here.                    */
using System;
/*  Create an alias for functions of the form f: R -> R.                      */
using function = System.Func<double, double>;
/*  Class providing an implementation of the bisection method.                */
class Bisection {
    /*  The error after n iterations is |b - a| / 2^n. Since double has a     *
     *  52-bit mantissa, if |b - a| ~= 1, then after 52 steps we can halt the *
     *  program. To allow for |b - a| to be larger, we stop the process after *
     *  at most 64 iterations.                                                */
    const uint maximumNumberOfIterations = 64U;
    /*  The maximum allowed error. This is double precision epsilon.          */
    const double epsilon = 2.220446049250313E-16;
    /*  Computes the root of a function using the bisection method.           */
    static double Root(function f, double a, double b)
    {
        /*  Variable for keeping track of the number of iterations.           */
        uint iters;
        /*  The midpoint for the bisection. This updates as we iterate.       */
        double midpoint;
        /*  We do not require a < b, nor f(a) < f(b). We need one of          *
         *  these to evaluate negative under f and one to evaluate to         *
         *  positive. Call the negative entry left and positive one right.    */
        double left, right;
        /*  Evaluate f at the endpoints to determine which is positive        *
         *  and which is negative, transforming [a, b] to [left, right].      */
        double aEval = f(a);
        double bEval = f(b);
        /*  Rare case, f(a) = 0. Return a, no bisection needed.               */
        if (aEval == 0.0)
            return a;
        /*  Similarly, if f(b) = 0, then we found the root. Return b.         */
        if (bEval == 0.0)
            return b;
        /*  Compare the two evaluations and set the left and right values.    */
        if (aEval < bEval)
        {
            /*  If both evaluations are negative, or if both are positive,    *
             *  then the bisection method will not work. Return NaN.          */
            if (bEval < 0.0 || aEval > 0.0)
                return (a - a) / (a - a);
            /*  Otherwise, since f(a) < f(b), set left = a and right = b.     */
            left = a;
            right = b;
        }
        /*  In this case the function starts positive and goes negative.      */
        else
        {
            /*  Same sanity check as before. We need one evaluation to be     *
             *  negative and one to be positive. Abort if the signs agree.    */
            if (aEval < 0.0 || bEval > 0.0)
                return (a - a) / (a - a);
            /*  Since f(a) > f(b), set left = b and right = a.                */
            left = b;
            right = a;
        }
        /*  Start the bisection method. Compute the midpoint of a and b.      */
        midpoint = 0.5 * (a + b);
        /*  Iteratively divide the range in half to find the root.            */
        for (iters = 0; iters < maximumNumberOfIterations; ++iters)
        {
            /*  If f(x) is very small, we are close to a root and can         *
             *  break out of this for loop. Check for this.                   */
            double eval = f(midpoint);
            if (Math.Abs(eval) <= epsilon)
                break;
            /*  Apply bisection to get a better approximation. We have        *
             *  f(left) < 0 < f(right). If f(midpoint) < 0, replace the       *
             *  interval [left, right] with [midpoint, right]. Set left       *
             *  to the midpoint and set midpoint to be closer to right.       */
            if (eval < 0.0)
            {
                left = midpoint;
                midpoint = 0.5 * (midpoint + right);
            }
            /*  If f(midpoint) > 0, then replace right with the midpoint,     *
             *  changing [left, right] into [left, midpoint]. We then set     *
             *  the midpoint to be closer to left.                            */
            else
            {
                right = midpoint;
                midpoint = 0.5 * (left + midpoint);
            }
        }
        /*  After n iterations, we are at most |b - a| / 2^n from the         *
         *  root of the function. 1 / 2^n goes to zero very quickly,          *
         *  meaning the convergence is very quick.                            */
        return midpoint;
    }
    /*  End of root.                                                          */
    /*  Main routine used for testing our Bisection implementation.           */
    static void Main()
    {
        /*  pi is somewhere between 3 and 4, and it is a root to sine.        */
        const double a = 3.0;
        const double b = 4.0;
        /*  Compute pi using bisection. We should get pi = 3.14159...,        *
         *  accurate to about 16 decimals.                                    */
        double pi = Root(Math.Sin, a, b);
        Console.WriteLine($"pi = {pi}");
    }
}
 
D
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using bisection.                    *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/03/09                                                        *
 ******************************************************************************/
/*  Create an alias for real-valued functions f: R -> R.                      */
alias realfunc = double function(double) pure nothrow @nogc @safe;
/*  Computes the root of a function using the bisection method.               */
static double bisection_method(realfunc f, double a, double b)
{
    /*  Floating-point absolute value (fabs) provided here.                   */
    import std.math : fabs;
    /*  Tell the algorithm to stop after several iterations to avoid an       *
     *  infinite loop. Double precision numbers have 52 bits in the mantissa, *
     *  so if |b - a| ~= 1, after 52 iterations of bisection we will get as   *
     *  close as we can to the root. To allow for |b - a| to be larger, halt  *
     *  the algorithm after at most 64 steps.                                 */
    const uint maximum_number_of_iterations = 64U;
    /*  Getting exact roots is hard using floating-point numbers. Allow a     *
     *  tolerance in our computation. This value is double precision epsilon. */
    const double epsilon = 2.220446049250313E-16;
    /*  Variable for keeping track of how many iterations we have performed.  */
    uint iters;
    /*  The midpoint for the bisection method. This will update as we iterate.*/
    double midpoint;
    /*  We do not require a < b, nor do we require f(a) < f(b). We only need  *
     *  one of these to evaluate to a negative under f and one to evaluate to *
     *  positive. We will call the negative entry left and positive one right.*/
    double left, right;
    /*  Evaluate f at the two endpoints to determine which is positive and    *
     *  which is negative. We transform [a, b] to [left, right] by doing this.*/
    const double a_eval = f(a);
    const double b_eval = f(b);
    /*  Rare case, f(a) = 0. Return a, no bisection needed.                   */
    if (a_eval == 0.0)
        return a;
    /*  Similarly, if f(b) = b, then we have already found the root. Return b.*/
    if (b_eval == 0.0)
        return b;
    /*  Compare the two evaluations and set left and right accordingly.       */
    if (a_eval < b_eval)
    {
        /*  If both evaluations are negative, or if both are positive, then   *
         *  the bisection method will not work. Return NaN.                   */
        if (b_eval < 0.0 || a_eval > 0.0)
            return (a - a) / (a - a);
        /*  Otherwise, since f(a) < f(b), set left = a and right = b.         */
        left = a;
        right = b;
    }
    /*  In this case the function starts positive and tends to a negative.    */
    else
    {
        /*  Same sanity check as before. We need one evaluation to be         *
         *  negative and one to be positive. Abort if both have the same sign.*/
        if (a_eval < 0.0 || b_eval > 0.0)
            return (a - a) / (a - a);
        /*  Since f(a) > f(b), set left = b and right = a.                    */
        left = b;
        right = a;
    }
    /*  Start the bisection method. Compute the midpoint of a and b.          */
    midpoint = 0.5 * (a + b);
    /*  Iteratively divide the range in half to find the root.                */
    for (iters = 0; iters < maximum_number_of_iterations; ++iters)
    {
        /*  If f(x) is very small, we are close to a root and can break out   *
         *  of this for loop. Check for this.                                 */
        const double eval = f(midpoint);
        if (fabs(eval) <= epsilon)
            break;
        /*  Apply bisection to get a better approximation for the root. We    *
         *  have f(left) < 0 < f(right). If f(midpoint) < 0, replace the      *
         *  interval [left, right] with [midpoint, right]. Set left to the    *
         *  midpoint and reset the midpoint to be closer to right.            */
        if (eval < 0.0)
        {
            left = midpoint;
            midpoint = 0.5 * (midpoint + right);
        }
        /*  In the other case, f(midpoint) > 0, we replace right with the     *
         *  midpoint, changing [left, right] into [left, midpoint]. We then   *
         *  set the midpoint to be closer to left.                            */
        else
        {
            right = midpoint;
            midpoint = 0.5 * (left + midpoint);
        }
    }
    /*  After n iterations, we are no more than |b - a| / 2^n away from the   *
     *  root of the function. 1 / 2^n goes to zero very quickly, meaning the  *
     *  convergence is very quick.                                            */
    return midpoint;
}
/*  End of bisection_method.                                                  */
/*  Main routine used for testing our implementation of the bisection method. */
int main()
{
    /*  stdio contains "printf", used for printing text.                      */
    import std.stdio : printf;
    /*  We will compute pi via bisection of sine on the interval [3, 4].      */
    import std.math : sin;
    /*  pi is somewhere between 3 and 4, and it is a root to sine.            */
    const double a = 3.0;
    const double b = 4.0;
    /*  Compute pi using bisection. We should get pi = 3.14159..., accurate   *
     *  to about 16 decimals.                                                 */
    const double pi = bisection_method(&sin, a, b);
    printf("pi = %.16f\n", pi);
    return 0;
}
 
Fortran
!------------------------------------------------------------------------------!
!                                   LICENSE                                    !
!------------------------------------------------------------------------------!
!   This file is part of mitx_mathematics_programming_examples.                !
!                                                                              !
!   mitx_mathematics_programming_examples is free software: you can            !
!   redistribute it and/or modify it under the terms of the GNU General Public !
!   License as published by the Free Software Foundation, either version 3 of  !
!   the License, or (at your option) any later version.                        !
!                                                                              !
!   mitx_mathematics_programming_examples is distributed in the hope that it   !
!   will be useful but WITHOUT ANY WARRANTY; without even the implied warranty !
!   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           !
!   GNU General Public License for more details.                               !
!                                                                              !
!   You should have received a copy of the GNU General Public License          !
!   along with mitx_mathematics_programming_examples.  If not, see             !
!   <https://www.gnu.org/licenses/>.                                           !
!------------------------------------------------------------------------------!
!   Purpose:                                                                   !
!       Calculates roots using the bisection method.                           !
!------------------------------------------------------------------------------!
!   Author: Ryan Maguire                                                       !
!   Date:   2025/03/09                                                         !
!------------------------------------------------------------------------------!
MODULE BISECTION
    IMPLICIT NONE
    ! Given a continuous function defined on [a, b], the nth iteration of the
    ! bisection method is at most |b - a| / 2^n away from the root. A double
    ! precision number has 52 bits in the mantissa, meaning if |b - a| ~= 1,
    ! then after 52 iterations we are as close to the root as we can get.
    ! To allow for a larger range, halt the algorithm after 64 iterations.
    INTEGER :: MAXIMUM_NUMBER_OF_ITERATIONS = 64
    ! Maximum allowed error. This is double precision epsilon.
    REAL :: EPSILON = 2.220446049250313E-16
    CONTAINS
    !--------------------------------------------------------------------------!
    !   Function:                                                              !
    !       BISECTION_METHOD                                                   !
    !   Purpose:                                                               !
    !       Computes roots using the bisection method.                         !
    !   Arguments:                                                             !
    !       FUNC (FUNCTION):                                                   !
    !           A function from [A, B] to the real numbers.                    !
    !       A (REAL):                                                          !
    !           One of the endpoints for the interval.                         !
    !       B (REAL):                                                          !
    !           The other the endpoint for the interval.                       !
    !   OUTPUT:                                                                !
    !       ROOT (REAL):                                                       !
    !           A root for FUNC.                                               !
    !--------------------------------------------------------------------------!
    FUNCTION BISECTION_METHOD(FUNC, A, B)
        IMPLICIT NONE
        ! FUNC is a function f: [A, B] -> Reals. Define this.
        INTERFACE
            FUNCTION FUNC(X)
                IMPLICIT NONE
                REAL, INTENT(IN) :: X
                REAL :: FUNC
            END FUNCTION FUNC
        END INTERFACE
        ! The inputs are positive real numbers.
        REAL, INTENT(IN) :: A, B
        ! The output is also a positive real number, the root of FUNC.
        REAL :: BISECTION_METHOD
        ! Dummy variable for keeping track of how many iterations we've done.
        INTEGER :: ITERS
        ! The midpoint of the interval. The bisection method iteratively cuts
        ! the interval in half to find the root.
        REAL :: MIDPOINT
        ! The endpoints of the current interval. These are updated with each
        ! iteration. We initially define LEFT so that FUNC(LEFT) < 0, and
        ! similarly choose RIGHT to make 0 < FUNC(RIGHT).
        REAL :: LEFT, RIGHT
        ! The evaluations of A and B are used to determine LEFT and RIGHT.
        REAL :: A_EVAL, B_EVAL
        ! At each iteration we compute FUNC(MIDPOINT) and reset the interval
        ! depending on whether or not the value is positive.
        REAL :: EVAL
        ! Initial setup, find out which evaluation is positive and which is
        ! negative. If the signs of the two agree we treat this as an error.
        A_EVAL = FUNC(A)
        B_EVAL = FUNC(B)
        ! Special case, A is a root. Set the output to A and return.
        IF (ABS(A_EVAL) .LE. EPSILON) THEN
            BISECTION_METHOD = A
            RETURN
        END IF
        ! Similarly for B, if FUNC(B) ~= 0, return B.
        IF (ABS(B_EVAL) .LE. EPSILON) THEN
            BISECTION_METHOD = B
            RETURN
        END IF
        ! If FUNC(A) < 0 < FUNC(B), then LEFT = A and RIGHT = B.
        IF (A_EVAL .LT. B_EVAL) THEN
            ! If FUNC(A) and FUNC(B) have the same sign (both positive or
            ! both negative), return NaN. Bisection is undefined.
            IF ((A_EVAL .GT. 0) .OR. (B_EVAL .LT. 0)) THEN
                BISECTION_METHOD = (A - A) / (A - A)
                RETURN
            END IF
            LEFT = A
            RIGHT = B
        ! If FUNC(B) < 0 < FUNC(A), then LEFT = B and RIGHT = A.
        ELSE
            ! Same sanity check as before, make sure the signs are different.
            IF ((A_EVAL .LT. 0) .OR. (B_EVAL .GT. 0)) THEN
                BISECTION_METHOD = (A - A) / (A - A)
                RETURN
            END IF
            LEFT = B
            RIGHT = A
        END IF
        MIDPOINT = 0.5 * (A + B)
        ! Iteratively perform the bisection method.
        DO ITERS = 0, MAXIMUM_NUMBER_OF_ITERATIONS
            ! The interval is cut in half based on the sign of FUNC(MIDPOINT).
            ! Compute this and compare.
            EVAL = FUNC(MIDPOINT)
            ! If MIDPOINT is close to a root we can exit the function.
            IF (ABS(EVAL) .LE. EPSILON) THEN
                BISECTION_METHOD = MIDPOINT
                RETURN
            END IF
            ! Otherwise, divide the range in half. If EVAL is negative we
            ! replace LEFT with MIDPOINT and move MIDPOINT closer to RIGHT.
            IF (EVAL .LT. 0) THEN
                LEFT = MIDPOINT
                MIDPOINT = 0.5 * (MIDPOINT + RIGHT)
            ! Likewise, if EVAL is positive, replace RIGHT with MIDPOINT and
            ! move MIDPOINT closer to LEFT.
            ELSE
                RIGHT = MIDPOINT
                MIDPOINT = 0.5 * (LEFT + MIDPOINT)
            END IF
        END DO
        ! Provided |B - A| is not too big, we should now have a very good
        ! approximation to the root.
        BISECTION_METHOD = MIDPOINT
    END FUNCTION BISECTION_METHOD
END MODULE BISECTION
! Program for testing our implementation of the bisection method.
PROGRAM MAIN
    USE BISECTION
    IMPLICIT NONE
    ! We will compute pi by finding a root to sine on the interval [3, 4].
    ! The sine function is an intrinsic provided by Fortran.
    INTRINSIC SIN
    ! Pi is somewhere between 3 and 4, use this interval.
    REAL :: A = 3.0
    REAL :: B = 4.0
    ! Variable for the output.
    REAL :: PI
    ! Run the routine, compute pi, and print the result.
    PI = BISECTION_METHOD(SIN, A, B)
    PRINT "(A,F18.16)", "pi = ", PI
END PROGRAM MAIN
 
Go
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using bisection.                    *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/03/28                                                        *
 ******************************************************************************/
package main
/*  Only standard library imports are needed.                                 */
import (
    "fmt"   /*  Printf provided here, used for printing text to the screen.   */
    "math"  /*  Abs, floating-point absolute value function, found here.      */
)
/*  Function pointer notation is a little confusing. Create a typedef for it  *
 *  so we do not need to explicitly use it later.                             */
type realfunc func(x float64) float64
/*  Computes the root of a function using the bisection method.               */
func bisection_method(f realfunc, a float64, b float64) float64 {
    /*  Tell the algorithm to stop after several iterations to avoid an       *
     *  infinite loop. Double precision numbers have 52 bits in the mantissa, *
     *  so if |b - a| ~= 1, after 52 iterations of bisection we will get as   *
     *  close as we can to the root. To allow for |b - a| to be larger, halt  *
     *  the algorithm after at most 64 steps.                                 */
    const maximum_number_of_iterations uint32 = 64
    /*  Getting exact roots is hard using floating-point numbers. Allow a     *
     *  tolerance in our computation. This value is double precision epsilon. */
    const epsilon float64 = 2.220446049250313E-16
    /*  Variable for keeping track of how many iterations we have performed.  */
    var iters uint32
    /*  The midpoint for the bisection method. This will update as we iterate.*/
    var midpoint float64
    /*  We do not require a < b, nor do we require f(a) < f(b). We only need  *
     *  one of these to evaluate to a negative under f and one to evaluate to *
     *  positive. We will call the negative entry left and positive one right.*/
    var left, right float64
    /*  Evaluate f at the two endpoints to determine which is positive and    *
     *  which is negative. We transform [a, b] to [left, right] by doing this.*/
    var a_eval = f(a)
    var b_eval = f(b)
    /*  Rare case, f(a) = 0. Return a, no bisection needed.                   */
    if a_eval == 0.0 {
        return a
    }
    /*  Similarly, if f(b) = 0, then we have already found the root. Return b.*/
    if b_eval == 0.0 {
        return b
    }
    /*  Compare the two evaluations and set left and right accordingly.       */
    if a_eval < b_eval {
        /*  If both evaluations are negative, or if both are positive, then   *
         *  the bisection method will not work. Return NaN.                   */
        if b_eval < 0.0 || a_eval > 0.0 {
            return (a - a) / (a - a)
        }
        /*  Otherwise, since f(a) < f(b), set left = a and right = b.         */
        left = a
        right = b
    /*  In this case the function starts positive and tends to a negative.    */
    } else {
        /*  Same sanity check as before. We need one evaluation to be         *
         *  negative and one to be positive. Abort if both have the same sign.*/
        if a_eval < 0.0 || b_eval > 0.0 {
            return (a - a) / (a - a)
        }
        /*  Since f(a) > f(b), set left = b and right = a.                    */
        left = b
        right = a
    }
    /*  Start the bisection method. Compute the midpoint of a and b.          */
    midpoint = 0.5 * (a + b)
    /*  Iteratively divide the range in half to find the root.                */
    for iters = 0; iters < maximum_number_of_iterations; iters += 1 {
        /*  If f(x) is very small, we are close to a root and can break out   *
         *  of this for loop. Check for this.                                 */
        var eval = f(midpoint)
        if math.Abs(eval) <= epsilon {
            break
        }
        /*  Apply bisection to get a better approximation for the root. We    *
         *  have f(left) < 0 < f(right). If f(midpoint) < 0, replace the      *
         *  interval [left, right] with [midpoint, right]. Set left to the    *
         *  midpoint and reset the midpoint to be closer to right.            */
        if eval < 0.0 {
            left = midpoint
            midpoint = 0.5 * (midpoint + right)
        /*  In the other case, f(midpoint) > 0, we replace right with the     *
         *  midpoint, changing [left, right] into [left, midpoint]. We then   *
         *  set the midpoint to be closer to left.                            */
        } else {
            right = midpoint
            midpoint = 0.5 * (left + midpoint)
        }
    }
    /*  After n iterations, we are no more than |b - a| / 2^n away from the   *
     *  root of the function. 1 / 2^n goes to zero very quickly, meaning the  *
     *  convergence is very quick.                                            */
    return midpoint
}
/*  End of bisection_method.                                                  */
/*  Main routine used for testing our implementation of the bisection method. */
func main() {
    /*  pi is somewhere between 3 and 4, and it is a root to sine.            */
    const a float64 = 3.0
    const b float64 = 4.0
    /*  Compute pi using bisection. We should get pi = 3.14159..., accurate   *
     *  to about 16 decimals.                                                 */
    var pi = bisection_method(math.Sin, a, b)
    fmt.Printf("pi = %.16f\n", pi)
}
 
Java
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using the bisection method.         *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/03/28                                                        *
 ******************************************************************************/
/*  Class providing an implementation of the bisection method.                */
final public class Bisection {
    /*  Interface used for defining functions of the type f: R -> R.          */
    interface Function
    {
        double evaluate(double x);
    }
    /*  The error after n iterations is |b - a| / 2^n. Since double has a     *
     *  52-bit mantissa, if |b - a| ~= 1, then after 52 steps we can halt the *
     *  program. To allow for |b - a| to be larger, we stop the process after *
     *  at most 64 iterations.                                                */
    static private final int maximumNumberOfIterations = 64;
    /*  The maximum allowed error. This is double precision epsilon.          */
    static private final double epsilon = 2.220446049250313E-16;
    /*  Computes the root of a function using the bisection method.           */
    static double root(Function f, double a, double b)
    {
        /*  Variable for keeping track of the number of iterations.           */
        int iters;
        /*  The midpoint for the bisection. This updates as we iterate.       */
        double midpoint;
        /*  We do not require a < b, nor f(a) < f(b). We need one of          *
         *  these to evaluate negative under f and one to evaluate to         *
         *  positive. Call the negative entry left and positive one right.    */
        double left, right;
        /*  Evaluate f at the endpoints to determine which is positive        *
         *  and which is negative, transforming [a, b] to [left, right].      */
        double aEval = f.evaluate(a);
        double bEval = f.evaluate(b);
        /*  Rare case, f(a) = 0. Return a, no bisection needed.               */
        if (aEval == 0.0)
            return a;
        /*  Similarly, if f(b) = 0, then we found the root. Return b.         */
        if (bEval == 0.0)
            return b;
        /*  Compare the two evaluations and set the left and right values.    */
        if (aEval < bEval)
        {
            /*  If both evaluations are negative, or if both are positive,    *
             *  then the bisection method will not work. Return NaN.          */
            if (bEval < 0.0 || aEval > 0.0)
                return (a - a) / (a - a);
            /*  Otherwise, since f(a) < f(b), set left = a and right = b.     */
            left = a;
            right = b;
        }
        /*  In this case the function starts positive and goes negative.      */
        else
        {
            /*  Same sanity check as before. We need one evaluation to be     *
             *  negative and one to be positive. Abort if the signs agree.    */
            if (aEval < 0.0 || bEval > 0.0)
                return (a - a) / (a - a);
            /*  Since f(a) > f(b), set left = b and right = a.                */
            left = b;
            right = a;
        }
        /*  Start the bisection method. Compute the midpoint of a and b.      */
        midpoint = 0.5 * (a + b);
        /*  Iteratively divide the range in half to find the root.            */
        for (iters = 0; iters < maximumNumberOfIterations; ++iters)
        {
            /*  If f(x) is very small, we are close to a root and can         *
             *  break out of this for loop. Check for this.                   */
            double eval = f.evaluate(midpoint);
            if (Math.abs(eval) <= epsilon)
                break;
            /*  Apply bisection to get a better approximation. We have        *
             *  f(left) < 0 < f(right). If f(midpoint) < 0, replace the       *
             *  interval [left, right] with [midpoint, right]. Set left       *
             *  to the midpoint and set midpoint to be closer to right.       */
            if (eval < 0.0)
            {
                left = midpoint;
                midpoint = 0.5 * (midpoint + right);
            }
            /*  If f(midpoint) > 0, then replace right with the midpoint,     *
             *  changing [left, right] into [left, midpoint]. We then set     *
             *  the midpoint to be closer to left.                            */
            else
            {
                right = midpoint;
                midpoint = 0.5 * (left + midpoint);
            }
        }
        /*  After n iterations, we are at most |b - a| / 2^n from the         *
         *  root of the function. 1 / 2^n goes to zero very quickly,          *
         *  meaning the convergence is very quick.                            */
        return midpoint;
    }
    /*  End of root.                                                          */
    /*  Main routine used for testing our Bisection implementation.           */
    static public void main(String[] args)
    {
        /*  pi is somewhere between 3 and 4, and it is a root to sine.        */
        final double a = 3.0;
        final double b = 4.0;
        /*  Create a function that evaluates sine using Math.sin.             */
        Function sin = new Function()
        {
            /*  The evaluator simply calls the standard library function.     */
            public double evaluate(double x)
            {
                return Math.sin(x);
            }
        };
        /*  Compute pi using bisection. We should get pi = 3.14159...,        *
         *  accurate to about 16 decimals.                                    */
        double pi = Bisection.root(sin, a, b);
        System.out.printf("pi = %.16f\n", pi);
    }
}
 
Julia
################################################################################
#                                   LICENSE                                    #
################################################################################
#   This file is part of mitx_mathematics_programming_examples.                #
#                                                                              #
#   mitx_mathematics_programming_examples is free software: you can            #
#   redistribute it and/or modify it under the terms of the GNU General Public #
#   License as published by the Free Software Foundation, either version 3 of  #
#   the License, or (at your option) any later version.                        #
#                                                                              #
#   mitx_mathematics_programming_examples is distributed in the hope that it   #
#   will be useful, but WITHOUT ANY WARRANTY; without even the implied         #
#   warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  #
#   GNU General Public License for more details.                               #
#                                                                              #
#   You should have received a copy of the GNU General Public License          #
#   along with mitx_mathematics_programming_examples.  If not, see             #
#   <https://www.gnu.org/licenses/>.                                           #
################################################################################
#   Purpose:                                                                   #
#       Calculates roots of a function using bisection.                        #
################################################################################
#   Author:     Ryan Maguire                                                   #
#   Date:       March 29, 2025.                                                #
################################################################################
################################################################################
#   Function:                                                                  #
#       bisection                                                              #
#   Purpose:                                                                   #
#       Computes the root of a function f between a and b.                     #
#   Arguments:                                                                 #
#       f (function):                                                          #
#           A function f: R -> R. The root of f is computed.                   #
#       a (real):                                                              #
#           One of the endpoints of the interval for f.                        #
#       b (real):                                                              #
#           The other endpoint for f.                                          #
#   Output:                                                                    #
#       root (real):                                                           #
#           A root of the function f between a and b.                          #
################################################################################
function bisection(f, a, b)
    # Tell the algorithm to stop after several iterations to avoid an
    # infinite loop. Double precision numbers have 52 bits in the mantissa,
    # so if |b - a| ~= 1, after 52 iterations of bisection we will get as
    # close as we can to the root. To allow for |b - a| to be larger, halt
    # the algorithm after at most 64 steps.
    maximum_number_of_iterations = 64
    # The maximum allowed error. This is double precision epsilon.
    epsilon = 2.220446049250313E-16
    # Evaluate f at the two endpoints to determine which is positive and
    # which is negative. We transform [a, b] to [left, right] by doing this.
    a_eval = f(a)
    b_eval = f(b)
    # Rare case, f(a) = 0. Return a, no bisection needed.
    if a_eval == 0.0
        return a
    end
    # Similarly, if f(b) = 0, then we have already found the root. Return b.
    if b_eval == 0.0
        return b
    end
    # Compare the two evaluations and set left and right accordingly.
    if a_eval < b_eval
        # If both evaluations are negative, or if both are positive, then
        # the bisection method will not work. Return NaN.
        if b_eval < 0.0 || a_eval > 0.0
            return (a - a) / (a - a)
        end
        # Otherwise, since f(a) < f(b), set left = a and right = b.
        left = a
        right = b
    # In this case the function starts positive and tends to a negative.
    else
        # Same sanity check as before. We need one evaluation to be
        # negative and one to be positive. Abort if both have the same sign.
        if a_eval < 0.0 || b_eval > 0.0
            return (a - a) / (a - a)
        end
        # Since f(a) > f(b), set left = b and right = a.
        left = b
        right = a
    end
    # Start the bisection method. Compute the midpoint of a and b.
    midpoint = 0.5 * (a + b)
    # Iteratively apply the bisection method.
    for _ = 1:maximum_number_of_iterations
        # If f(x) is very small, we are close to a root and can break out
        # of this for loop. Check for this.
        eval = f(midpoint)
        if abs(eval) <= epsilon
            break
        end
        # Apply bisection to get a better approximation for the root. We
        # have f(left) < 0 < f(right). If f(midpoint) < 0, replace the
        # interval [left, right] with [midpoint, right]. Set left to the
        # midpoint and reset the midpoint to be closer to right.
        if eval < 0.0
            left = midpoint
            midpoint = 0.5 * (midpoint + right)
        # In the other case, f(midpoint) > 0, we replace right with the
        # midpoint, changing [left, right] into [left, midpoint]. We then
        # set the midpoint to be closer to left.
        else
            right = midpoint
            midpoint = 0.5 * (left + midpoint)
        end
    end
    # After n iterations, we are no more than |b - a| / 2^n away from the
    # root of the function. 1 / 2^n goes to zero very quickly, meaning the
    # convergence is very quick.
    return midpoint
end
# pi is somewhere between 3 and 4, and it is a root to sine.
a = 3.0
b = 4.0
# Compute pi using bisection. We should get pi = 3.14159..., accurate
# to about 16 decimals.
pi_by_bisection = bisection(sin, a, b)
println("pi = ", pi_by_bisection)
 
JavaScript
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using bisection.                    *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/05/18                                                        *
 ******************************************************************************/
/*  Computes the root of a function using the bisection method.               */
function bisectionMethod(f, a, b) {
    /*  Tell the algorithm to stop after several iterations to avoid an       *
     *  infinite loop. Double precision numbers have 52 bits in the mantissa, *
     *  so if |b - a| ~= 1, after 52 iterations of bisection we will get as   *
     *  close as we can to the root. To allow for |b - a| to be larger, halt  *
     *  the algorithm after at most 64 steps.                                 */
    const MAXIMUM_NUMBER_OF_ITERATIONS = 64;
    /*  Getting exact roots is hard using floating-point numbers. Allow a     *
     *  tolerance in our computation. This value is double precision epsilon. */
    const EPSILON = 2.220446049250313E-16;
    /*  The midpoint for the bisection method. This will update as we iterate.*/
    let midpoint;
    /*  Variable for keeping track of the number of iterations performed.     */
    let iters;
    /*  We do not require a < b, nor do we require f(a) < f(b). We only need  *
     *  one of these to evaluate to a negative under f and one to evaluate to *
     *  positive. We will call the negative entry left and positive one right.*/
    let left;
    let right;
    /*  Evaluate f at the two endpoints to determine which is positive and    *
     *  which is negative. We transform [a, b] to [left, right] by doing this.*/
    const A_EVAL = f(a);
    const B_EVAL = f(b);
    /*  Rare case, f(a) = 0. Return a, no bisection needed.                   */
    if (A_EVAL == 0.0) {
        return a;
    }
    /*  Similarly, if f(b) = 0, then we have already found the root. Return b.*/
    if (B_EVAL == 0.0) {
        return b;
    }
    /*  Compare the two evaluations and set left and right accordingly.       */
    if (A_EVAL < B_EVAL) {
        /*  If both evaluations are negative, or if both are positive, then   *
         *  the bisection method will not work. Return NaN.                   */
        if ((B_EVAL < 0.0) || (A_EVAL > 0.0)) {
            return (a - a) / (a - a);
        }
        /*  Otherwise, since f(a) < f(b), set left = a and right = b.         */
        left = a;
        right = b;
    /*  In this case the function starts positive and tends to a negative.    */
    } else {
        /*  Same sanity check as before. We need one evaluation to be         *
         *  negative and one to be positive. Abort if both have the same sign.*/
        if ((A_EVAL < 0.0) || (B_EVAL > 0.0)) {
            return (a - a) / (a - a);
        }
        /*  Since f(a) > f(b), set left = b and right = a.                    */
        left = b;
        right = a;
    }
    /*  Start the bisection method. Compute the midpoint of a and b.          */
    midpoint = 0.5 * (a + b);
    /*  Iteratively divide the range in half to find the root.                */
    for (iters = 0; iters < MAXIMUM_NUMBER_OF_ITERATIONS; ++iters) {
        /*  If f(x) is very small, we are close to a root and can break out   *
         *  of this for loop. Check for this.                                 */
        const EVAL = f(midpoint);
        if (Math.abs(EVAL) <= EPSILON) {
            break;
        }
        /*  Apply bisection to get a better approximation for the root. We    *
         *  have f(left) < 0 < f(right). If f(midpoint) < 0, replace the      *
         *  interval [left, right] with [midpoint, right]. Set left to the    *
         *  midpoint and reset the midpoint to be closer to right.            */
        if (EVAL < 0.0) {
            left = midpoint;
            midpoint = 0.5 * (midpoint + right);
        /*  In the other case, f(midpoint) > 0, we replace right with the     *
         *  midpoint, changing [left, right] into [left, midpoint]. We then   *
         *  set the midpoint to be closer to left.                            */
        } else {
            right = midpoint;
            midpoint = 0.5 * (left + midpoint);
        }
    }
    /*  After n iterations, we are no more than |b - a| / 2^n away from the   *
     *  root of the function. 1 / 2^n goes to zero very quickly, meaning the  *
     *  convergence is very quick.                                            */
    return midpoint;
}
/*  End of bisectionMethod.                                                   */
/*  pi is somewhere between 3 and 4, and it is a root to sine.                */
const A = 3.0;
const B = 4.0;
/*  Compute pi using bisection. We should get pi = 3.14159..., accurate       *
 *  to about 16 decimals.                                                     */
const PI = bisectionMethod(Math.sin, A, B);
console.log("pi = " + PI.toFixed(16));
 
Kotlin
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using bisection.                    *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/05/18                                                        *
 ******************************************************************************/
import kotlin.math.abs
import kotlin.math.sin
/*  Function pointer notation is a little confusing. Create a typedef for it  *
 *  so we do not need to explicitly use it later.                             */
typealias RealFunc = (Double) -> Double
/*  Computes the root of a function using the bisection method.               */
fun bisectionMethod(f: RealFunc, a: Double, b: Double): Double {
    /*  Tell the algorithm to stop after several iterations to avoid an       *
     *  infinite loop. Double precision numbers have 52 bits in the mantissa, *
     *  so if |b - a| ~= 1, after 52 iterations of bisection we will get as   *
     *  close as we can to the root. To allow for |b - a| to be larger, halt  *
     *  the algorithm after at most 64 steps.                                 */
    val maximum_number_of_iterations: Int = 64
    /*  Getting exact roots is hard using floating-point numbers. Allow a     *
     *  tolerance in our computation. This value is double precision epsilon. */
    val epsilon: Double = 2.220446049250313E-16
    /*  The midpoint for the bisection method. This will update as we iterate.*/
    var midpoint: Double
    /*  We do not require a < b, nor do we require f(a) < f(b). We only need  *
     *  one of these to evaluate to a negative under f and one to evaluate to *
     *  positive. We will call the negative entry left and positive one right.*/
    var left: Double
    var right: Double
    /*  Evaluate f at the two endpoints to determine which is positive and    *
     *  which is negative. We transform [a, b] to [left, right] by doing this.*/
    val aEval: Double = f(a)
    val bEval: Double = f(b)
    /*  Rare case, f(a) = 0. Return a, no bisection needed.                   */
    if (aEval == 0.0) {
        return a
    }
    /*  Similarly, if f(b) = 0, then we have already found the root. Return b.*/
    if (bEval == 0.0) {
        return b
    }
    /*  Compare the two evaluations and set left and right accordingly.       */
    if (aEval < bEval) {
        /*  If both evaluations are negative, or if both are positive, then   *
         *  the bisection method will not work. Return NaN.                   */
        if ((bEval < 0.0) || (aEval > 0.0)) {
            return (a - a) / (a - a)
        }
        /*  Otherwise, since f(a) < f(b), set left = a and right = b.         */
        left = a
        right = b
    /*  In this case the function starts positive and tends to a negative.    */
    } else {
        /*  Same sanity check as before. We need one evaluation to be         *
         *  negative and one to be positive. Abort if both have the same sign.*/
        if ((aEval < 0.0) || (bEval > 0.0)) {
            return (a - a) / (a - a)
        }
        /*  Since f(a) > f(b), set left = b and right = a.                    */
        left = b
        right = a
    }
    /*  Start the bisection method. Compute the midpoint of a and b.          */
    midpoint = 0.5 * (a + b)
    /*  Iteratively divide the range in half to find the root.                */
    for (iters in 0 .. maximum_number_of_iterations) {
        /*  If f(x) is very small, we are close to a root and can break out   *
         *  of this for loop. Check for this.                                 */
        val eval: Double = f(midpoint)
        if (abs(eval) <= epsilon) {
            break
        }
        /*  Apply bisection to get a better approximation for the root. We    *
         *  have f(left) < 0 < f(right). If f(midpoint) < 0, replace the      *
         *  interval [left, right] with [midpoint, right]. Set left to the    *
         *  midpoint and reset the midpoint to be closer to right.            */
        if (eval < 0.0) {
            left = midpoint
            midpoint = 0.5 * (midpoint + right)
        /*  In the other case, f(midpoint) > 0, we replace right with the     *
         *  midpoint, changing [left, right] into [left, midpoint]. We then   *
         *  set the midpoint to be closer to left.                            */
        } else {
            right = midpoint
            midpoint = 0.5 * (left + midpoint)
        }
    }
    /*  After n iterations, we are no more than |b - a| / 2^n away from the   *
     *  root of the function. 1 / 2^n goes to zero very quickly, meaning the  *
     *  convergence is very quick.                                            */
    return midpoint
}
/*  End of bisection_method.                                                  */
/*  Test out our function, compute the square root of 2.                      */
fun main() {
    /*  pi is somewhere between 3 and 4, and it is a root to sine.            */
    val a: Double = 3.0
    val b: Double = 4.0
    /*  Compute pi using bisection. We should get pi = 3.14159..., accurate   *
     *  to about 16 decimals.                                                 */
    val pi: Double = bisectionMethod(::sin, a, b)
    println("pi = $pi")
}
 
MATLAB
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                   LICENSE                                    %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   This file is part of mitx_mathematics_programming_examples.                %
%                                                                              %
%   mitx_mathematics_programming_examples is free software: you can            %
%   redistribute it and/or modify it under the terms of the GNU General Public %
%   License as published by the Free Software Foundation, either version 3 of  %
%   the License, or (at your option) any later version.                        %
%                                                                              %
%   mitx_mathematics_programming_examples is distributed in the hope that it   %
%   will be useful, but WITHOUT ANY WARRANTY; without even the implied         %
%   warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  %
%   GNU General Public License for more details.                               %
%                                                                              %
%   You should have received a copy of the GNU General Public License          %
%   along with mitx_mathematics_programming_examples.  If not, see             %
%   <https://www.gnu.org/licenses/>.                                           %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   Purpose:                                                                   %
%       Calculates roots of a function using bisection.                        %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   Author:     Ryan Maguire                                                   %
%   Date:       March 29, 2025.                                                %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   Function:                                                                  %
%       bisection                                                              %
%   Purpose:                                                                   %
%       Computes the root of a function f between a and b.                     %
%   Arguments:                                                                 %
%       f (function):                                                          %
%           A function f: R -> R. The root of f is computed.                   %
%       a (real):                                                              %
%           One of the endpoints of the interval for f.                        %
%       b (real):                                                              %
%           The other endpoint for f.                                          %
%   Output:                                                                    %
%       root (real):                                                           %
%           A root of the function f between a and b.                          %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function root = bisection(f, a, b)
    % Tell the algorithm to stop after several iterations to avoid an
    % infinite loop. Double precision numbers have 52 bits in the mantissa,
    % so if |b - a| ~= 1, after 52 iterations of bisection we will get as
    % close as we can to the root. To allow for |b - a| to be larger, halt
    % the algorithm after at most 64 steps.
    maximum_number_of_iterations = 64;
    % The maximum allowed error. This is double precision epsilon.
    epsilon = 2.220446049250313E-16;
    % Evaluate f at the two endpoints to determine which is positive and
    % which is negative. We transform [a, b] to [left, right] by doing this.
    a_eval = f(a);
    b_eval = f(b);
    % Rare case, f(a) = 0. Return a, no bisection needed.
    if a_eval == 0.0
        root = a;
        return;
    end
    % Similarly, if f(b) = 0, then we have already found the root. Return b.
    if b_eval == 0.0
        root = b;
        return;
    end
    % Compare the two evaluations and set left and right accordingly.
    if a_eval < b_eval
        % If both evaluations are negative, or if both are positive, then
        % the bisection method will not work. Return NaN.
        if b_eval < 0.0 || a_eval > 0.0
            root = (a - a) / (a - a);
            return;
        end
        % Otherwise, since f(a) < f(b), set left = a and right = b.
        left = a;
        right = b;
    % In this case the function starts positive and tends to a negative.
    else
        % Same sanity check as before. We need one evaluation to be
        % negative and one to be positive. Abort if both have the same sign.
        if a_eval < 0.0 || b_eval > 0.0
            root = (a - a) / (a - a);
            return;
        end
        % Since f(a) > f(b), set left = b and right = a.
        left = b;
        right = a;
    end
    % Start the bisection method. Compute the midpoint of a and b.
    midpoint = 0.5 * (a + b);
    % Iteratively apply the bisection method.
    for _ = 1:maximum_number_of_iterations
        % If f(x) is very small, we are close to a root and can break out
        % of this for loop. Check for this.
        f_eval = f(midpoint);
        if abs(f_eval) <= epsilon
            break;
        end
        % Apply bisection to get a better approximation for the root. We
        % have f(left) < 0 < f(right). If f(midpoint) < 0, replace the
        % interval [left, right] with [midpoint, right]. Set left to the
        % midpoint and reset the midpoint to be closer to right.
        if f_eval < 0.0
            left = midpoint;
            midpoint = 0.5 * (midpoint + right);
        % In the other case, f(midpoint) > 0, we replace right with the
        % midpoint, changing [left, right] into [left, midpoint]. We then
        % set the midpoint to be closer to left.
        else
            right = midpoint;
            midpoint = 0.5 * (left + midpoint);
        end
    end
    % After n iterations, we are no more than |b - a| / 2^n away from the
    % root of the function. 1 / 2^n goes to zero very quickly, meaning the
    % convergence is very quick.
    root = midpoint;
end
% pi is somewhere between 3 and 4, and it is a root to sine.
a = 3.0;
b = 4.0;
% Compute pi using bisection. We should get pi = 3.14159..., accurate
% to about 16 decimals.
pi_by_bisection = bisection(@sin, a, b);
printf("pi = %.16f\n", pi_by_bisection);
 
Objective-C
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using the bisection method.         *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/03/29                                                        *
 ******************************************************************************/
/*  NSObject, base class for all Objective-C classes, found here. math.h and  *
 *  stdio.h are also included in this header file.                            */
#import <Foundation/Foundation.h>
/*  Function pointer notation is a little confusing. Create a typedef for it  *
 *  so we do not need to explicitly use it later.                             */
typedef double (*function)(double);
/*  Class providing an implementation of the bisection method.                */
@interface Bisection: NSObject
    {
        /*  How many iterations of the Bisection method we do before exiting. */
        const unsigned int maximumNumberOfIterations;
        /*  Allowed tolerance for the root-finding method.                    */
        const double epsilon;
    }
    /*  Given a function f: R -> R and two real numbers a and b, this finds   *
     *  a root of f between a and b using the bisection method.               */
    + (double) root: (function)f start: (double)a finish: (double)b;
@end
@implementation Bisection
    /*  The error after n iterations is |b - a| / 2^n. Since double has a     *
     *  52-bit mantissa, if |b - a| ~= 1, then after 52 steps we can halt the *
     *  program. To allow for |b - a| to be larger, we stop the process after *
     *  at most 64 iterations.                                                */
    static const unsigned int maximumNumberOfIterations = 64U;
    /*  The maximum allowed error. This is double precision epsilon.          */
    static const double epsilon = 2.220446049250313E-16;
    /*  Computes the root of a function using the bisection method.           */
    + (double) root: (function)f start: (double)a finish: (double)b
    {
        /*  Variable for keeping track of the number of iterations.           */
        unsigned int iters;
        /*  The midpoint for the bisection. This updates as we iterate.       */
        double midpoint;
        /*  We do not require a < b, nor f(a) < f(b). We need one of          *
         *  these to evaluate negative under f and one to evaluate to         *
         *  positive. Call the negative entry left and positive one right.    */
        double left, right;
        /*  Evaluate f at the endpoints to determine which is positive        *
         *  and which is negative, transforming [a, b] to [left, right].      */
        const double aEval = f(a);
        const double bEval = f(b);
        /*  Rare case, f(a) = 0. Return a, no bisection needed.               */
        if (aEval == 0.0)
            return a;
        /*  Similarly, if f(b) = 0, then we found the root. Return b.         */
        if (bEval == 0.0)
            return b;
        /*  Compare the two evaluations and set the left and right values.    */
        if (aEval < bEval)
        {
            /*  If both evaluations are negative, or if both are positive,    *
             *  then the bisection method will not work. Return NaN.          */
            if (bEval < 0.0 || aEval > 0.0)
                return (a - a) / (a - a);
            /*  Otherwise, since f(a) < f(b), set left = a and right = b.     */
            left = a;
            right = b;
        }
        /*  In this case the function starts positive and goes negative.      */
        else
        {
            /*  Same sanity check as before. We need one evaluation to be     *
             *  negative and one to be positive. Abort if the signs agree.    */
            if (aEval < 0.0 || bEval > 0.0)
                return (a - a) / (a - a);
            /*  Since f(a) > f(b), set left = b and right = a.                */
            left = b;
            right = a;
        }
        /*  Start the bisection method. Compute the midpoint of a and b.      */
        midpoint = 0.5 * (a + b);
        /*  Iteratively divide the range in half to find the root.            */
        for (iters = 0; iters < maximumNumberOfIterations; ++iters)
        {
            /*  If f(x) is very small, we are close to a root and can         *
             *  break out of this for loop. Check for this.                   */
            const double eval = f(midpoint);
            if (fabs(eval) <= epsilon)
                break;
            /*  Apply bisection to get a better approximation. We have        *
             *  f(left) < 0 < f(right). If f(midpoint) < 0, replace the       *
             *  interval [left, right] with [midpoint, right]. Set left       *
             *  to the midpoint and set midpoint to be closer to right.       */
            if (eval < 0.0)
            {
                left = midpoint;
                midpoint = 0.5 * (midpoint + right);
            }
            /*  If f(midpoint) > 0, then replace right with the midpoint,     *
             *  changing [left, right] into [left, midpoint]. We then set     *
             *  the midpoint to be closer to left.                            */
            else
            {
                right = midpoint;
                midpoint = 0.5 * (left + midpoint);
            }
        }
        /*  After n iterations, we are at most |b - a| / 2^n from the         *
         *  root of the function. 1 / 2^n goes to zero very quickly,          *
         *  meaning the convergence is very quick.                            */
        return midpoint;
    }
    /*  End of root.                                                          */
@end
/*  End of Bisection implementation.                                          */
/*  Main routine used for testing our implementation of the Bisection method. */
int main(void)
{
    /*  pi is somewhere between 3 and 4, and it is a root to sine.            */
    const double a = 3.0;
    const double b = 4.0;
    /*  Compute pi using bisection. We should get pi = 3.14159..., accurate   *
     *  to about 16 decimals.                                                 */
    const double pi = [Bisection root: sin start: a finish: b];
    printf("pi = %.16f\n", pi);
    return 0;
}
 
Pascal
(******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General Public*
 *  License as published by the Free Software Foundation, either version 3 of *
 *  the License, or (at your option) any later version.                       *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that it  *
 *  will be useful but WITHOUT ANY WARRANTY; without even the implied warranty*
 *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                              *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples.  If not, see            *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Computes roots using the bisection method.                            *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/04/17                                                        *
 ******************************************************************************)
PROGRAM Bisection;
(*  We define two constants: The allowed tolerance (or epsilon value),        *
 *  and the maximum number of iterations we allow for the bisection method.   *)
CONST
    (*  Tell the algorithm to stop after several iterations to avoid an       *
     *  infinite loop. Double precision numbers have 52 bits in the mantissa, *
     *  so if |b - a| ~= 1, after 52 iterations of bisection we will get as   *
     *  close as we can to the root. To allow for |b - a| to be larger, halt  *
     *  the algorithm after at most 64 steps.                                 *)
    MaximumNumberOfIterations: Integer = 64;
    (*  Maximum allowed error. This is double precision epsilon.              *)
    Epsilon: Real = 2.220446049250313E-16;
    (*  Pi is between 2 and 4 and is a root to sine.                          *)
    LeftValue: Real = 2.0;
    RightValue: Real = 4.0;
(*  Create an alias for real functions f: R -> R. This makes the syntax of    *
 *  the parameters for the BisectionMethod function a little more readable.   *)
TYPE
    RealFunc = Function(Const X: Real): Real;
(*  The main program only has one variable, pi computed via bisection.        *)
VAR
    PiByBisection: Real;
(*  Sin is a built-in procedure. Create a function Real -> Real that computes *
 *  sin(x) so that we may pass it as a parameter to BisectionMethod.          *)
Function Sine(const X: Real) : Real;
BEGIN
    Sine := SIN(X);
END;
(******************************************************************************
 *  Function:                                                                 *
 *      BisectionMethod                                                       *
 *  Purpose:                                                                  *
 *      Computes roots using the bisection method.                            *
 *   Arguments:                                                               *
 *       F (RealFunc):                                                        *
 *           A function from [A, B] to the real numbers.                      *
 *       A (Real):                                                            *
 *           One of the endpoints for the interval.                           *
 *       B (Real):                                                            *
 *           The other the endpoint for the interval.                         *
 *   OUTPUT:                                                                  *
 *       Root (Real):                                                         *
 *           A root for F between A and B.                                    *
 ******************************************************************************)
Function BisectionMethod(Const F: RealFunc; Const A, B: Real) : Real;
VAR
    (*  We do not require A < B, nor F(A) < F(B). We will use Left and Right  *
     *  to re-orient the interval so that F(Left) < F(Right). Midpoint will   *
     *  be the center of the interval, and these three values will be updated *
     *  iteratively as we perform the bisection method.                       *)
    Left, Right, Midpoint: Real;
    (*  Variables for the evaluation of F at A, B, and Midpoint, respectively.*)
    AEval, BEval, Eval: Real;
    (*  Dummy variable for tracking how many iterations we've performed.      *)
    Iters: Integer;
LABEL
    (*  Several spots allow for early returns in the function. We use a       *
     *  single label for GOTO to allow us to break out of the function.       *)
    Finished;
BEGIN
    (*  Initial setup, find out which evaluation is positive and which is     *
     *  negative. If the signs of the two agree we treat this as an error.    *)
    AEval := F(A);
    BEval := F(B);
    (*  Special case, A is a root. Set the output to A and return.            *)
    IF (ABS(AEval) < Epsilon) THEN
    BEGIN
        BisectionMethod := A;
        GOTO Finished;
    END;
    (*  Similarly for B, if F(B) ~= 0, return B.                              *)
    IF (ABS(BEval) < Epsilon) THEN
    BEGIN
        BisectionMethod := B;
        GOTO Finished;
    END;
    (*  If F(A) < 0 < F(B), then Left = A and Right = B.                      *)
    IF (AEval < BEval) THEN
    BEGIN
        (*  If F(A) and F(B) have the same sign (both positive or
         *  both negative), return NaN. Bisection is undefined.               *)
        IF ((AEval > 0) OR (BEval < 0)) THEN
        BEGIN
            BisectionMethod := (A - A) / (A - A);
            GOTO Finished;
        END;
        Left := A;
        Right := B;
    (*  If F(B) < 0 < F(A), then Left = B and Right = A.                      *)
    END
    ELSE
    BEGIN
        (*  Same sanity check as before, make sure the signs are different.   *)
        IF ((AEval < 0) OR (BEval > 0)) THEN
        BEGIN
            BisectionMethod := (A - A) / (A - A);
            GOTO Finished;
        END;
        Left := B;
        Right := A;
    END;
    Midpoint := 0.5 * (A + B);
    (*  Iteratively perform the bisection method.                             *)
    FOR Iters := 0 TO MaximumNumberOfIterations - 1 DO
    BEGIN
        (*  The interval is cut in half based on the sign of F(Midpoint).     *
         *  Compute this and compare.                                         *)
        Eval := F(Midpoint);
        (*  If MIDPOINT is close to a root we can exit the function.          *)
        IF (ABS(Eval) < Epsilon) THEN BREAK;
        (*  Otherwise, divide the range in half. If Eval is negative we       *
         *  replace Left with Midpoint and move Midpoint closer to Right.     *)
        IF (Eval < 0) THEN BEGIN
            Left := Midpoint;
            Midpoint := 0.5 * (Midpoint + Right);
        (*  Likewise, if Eval is positive, replace Right with Midpoint and    *
         *  move Midpoint closer to Left.                                     *)
        END ELSE BEGIN
            Right := Midpoint;
            Midpoint := 0.5 * (Left + Midpoint);
        END;
    END;
    (*  Provided |B - A| is not too big, we should now have a very good       *
     *  approximation to the root.                                            *)
    BisectionMethod := Midpoint;
    Finished:
END;
(*  Program for testing our implementation of the bisection method.           *)
BEGIN
    (*  Pi is between 2 and 4 and is a root to sine.                          *)
    PiByBisection := BisectionMethod(@Sine, LeftValue, RightValue);
    WriteLn('pi = ', PiByBisection:0:16);
END.
 
IDL
;------------------------------------------------------------------------------;
;                                   LICENSE                                    ;
;------------------------------------------------------------------------------;
;   This file is part of mitx_mathematics_programming_examples.                ;
;                                                                              ;
;   mitx_mathematics_programming_examples is free software: you can            ;
;   redistribute it and/or modify it under the terms of the GNU General Public ;
;   License as published by the Free Software Foundation, either version 3 of  ;
;   the License, or (at your option) any later version.                        ;
;                                                                              ;
;   mitx_mathematics_programming_examples is distributed in the hope that it   ;
;   will be useful but WITHOUT ANY WARRANTY; without even the implied warranty ;
;   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           ;
;   GNU General Public License for more details.                               ;
;                                                                              ;
;   You should have received a copy of the GNU General Public License          ;
;   along with mitx_mathematics_programming_examples.  If not, see             ;
;   <https://www.gnu.org/licenses/>.                                           ;
;------------------------------------------------------------------------------;
;   Purpose:                                                                   ;
;       Calculates roots using the bisection method.                           ;
;------------------------------------------------------------------------------;
;   Author: Ryan Maguire                                                       ;
;   Date:   2025/03/09                                                         ;
;------------------------------------------------------------------------------;
;------------------------------------------------------------------------------;
;   Function:                                                                  ;
;       BISECTION_METHOD                                                       ;
;   Purpose:                                                                   ;
;       Computes roots using the bisection method.                             ;
;   Arguments:                                                                 ;
;       FUNC (FUNCTION):                                                       ;
;           A function from [A, B] to the real numbers.                        ;
;       A (REAL):                                                              ;
;           One of the endpoints for the interval.                             ;
;       B (REAL):                                                              ;
;           The other the endpoint for the interval.                           ;
;   OUTPUT:                                                                    ;
;       ROOT (REAL):                                                           ;
;           A root for FUNC.                                                   ;
;------------------------------------------------------------------------------;
FUNCTION BISECTION_METHOD, FUNC, A, B
    ; Tells the compiler that integers should be 32 bits, not 16.
    COMPILE_OPT IDL2
    ; Error checking code.
    ON_ERROR, 2
    ; Given a continuous function defined on [a, b], the nth iteration of the
    ; bisection method is at most |b - a| / 2^n away from the root. A double
    ; precision number has 52 bits in the mantissa, meaning if |b - a| ~= 1,
    ; then after 52 iterations we are as close to the root as we can get.
    ; To allow for a larger range, halt the algorithm after 64 iterations.
    MAXIMUM_NUMBER_OF_ITERATIONS = 64
    ; Maximum allowed error. This is double precision epsilon.
    EPSILON = 2.220446049250313E-16
    ; Initial setup, find out which evaluation is positive and which is
    ; negative. If the signs of the two agree we treat this as an error.
    A_EVAL = CALL_FUNCTION(FUNC, A)
    B_EVAL = CALL_FUNCTION(FUNC, B)
    ; Special case, A is a root. Set the output to A and return.
    IF (ABS(A_EVAL) LE EPSILON) THEN BEGIN
        RETURN, A
    ENDIF
    ; Similarly for B, if FUNC(B) ~= 0, return B.
    IF (ABS(B_EVAL) LE EPSILON) THEN BEGIN
        RETURN, B
    ENDIF
    ; If FUNC(A) < 0 < FUNC(B), then LEFT = A and RIGHT = B.
    IF (A_EVAL LT B_EVAL) THEN BEGIN
        ; If FUNC(A) and FUNC(B) have the same sign (both positive or
        ; both negative), return NaN. Bisection is undefined.
        IF ((A_EVAL GT 0) OR (B_EVAL LT 0)) THEN BEGIN
            RETURN, (A - A) / (A - A)
        ENDIF
        LEFT = A
        RIGHT = B
    ; If FUNC(B) < 0 < FUNC(A), then LEFT = B and RIGHT = A.
    ENDIF ELSE BEGIN
        ; Same sanity check as before, make sure the signs are different.
        IF ((A_EVAL LT 0) OR (B_EVAL GT 0)) THEN BEGIN
            RETURN, (A - A) / (A - A)
        ENDIF
        LEFT = B
        RIGHT = A
    ENDELSE
    MIDPOINT = 0.5 * (A + B)
    ; Iteratively perform the bisection method.
    FOR ITERS = 0, MAXIMUM_NUMBER_OF_ITERATIONS DO BEGIN
        ; The interval is cut in half based on the sign of FUNC(MIDPOINT).
        ; Compute this and compare.
        EVAL = CALL_FUNCTION(FUNC, MIDPOINT)
        ; If MIDPOINT is close to a root we can exit the function.
        IF (ABS(EVAL) LE EPSILON) THEN BEGIN
            BREAK
        ENDIF
        ; Otherwise, divide the range in half. If EVAL is negative we
        ; replace LEFT with MIDPOINT and move MIDPOINT closer to RIGHT.
        IF (EVAL LT 0) THEN BEGIN
            LEFT = MIDPOINT
            MIDPOINT = 0.5 * (MIDPOINT + RIGHT)
        ; Likewise, if EVAL is positive, replace RIGHT with MIDPOINT and
        ; move MIDPOINT closer to LEFT.
        ENDIF ELSE BEGIN
            RIGHT = MIDPOINT
            MIDPOINT = 0.5 * (LEFT + MIDPOINT)
        ENDELSE
    END
    ; Provided |B - A| is not too big, we should now have a very good
    ; approximation to the root.
    RETURN, MIDPOINT
END
; Program for testing our implementation of the bisection method.
PRO MAIN
    ; Tells the compiler that integers should be 32 bits, not 16.
    COMPILE_OPT IDL2
    ; Pi is somewhere between 3 and 4, use this interval.
    A = DOUBLE(3.0)
    B = DOUBLE(4.0)
    ; Run the routine, compute pi, and print the result.
    PI_BY_BISECTION = BISECTION_METHOD("SIN", A, B)
    PRINT, PI_BY_BISECTION, FORMAT = 'PI = %18.16f'
END
 
Python
"""
################################################################################
#                                   LICENSE                                    #
################################################################################
#   This file is part of mitx_mathematics_programming_examples.                #
#                                                                              #
#   mitx_mathematics_programming_examples is free software: you can            #
#   redistribute it and/or modify it under the terms of the GNU General Public #
#   License as published by the Free Software Foundation, either version 3 of  #
#   the License, or (at your option) any later version.                        #
#                                                                              #
#   mitx_mathematics_programming_examples is distributed in the hope that it   #
#   will be useful, but WITHOUT ANY WARRANTY; without even the implied         #
#   warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  #
#   GNU General Public License for more details.                               #
#                                                                              #
#   You should have received a copy of the GNU General Public License          #
#   along with mitx_mathematics_programming_examples.  If not, see             #
#   <https://www.gnu.org/licenses/>.                                           #
################################################################################
#   Purpose:                                                                   #
#       Calculates roots of a function using bisection.                        #
################################################################################
#   Author:     Ryan Maguire                                                   #
#   Date:       April 17, 2025.                                                #
################################################################################
"""
# Pylint doesn't like "x" as a variable name. Disable this warning.
# pylint: disable = invalid-name
# sin provided here.
import math
# Computes the root of a function via bisection.
def bisection(f, a, b):
    """
        Function:
            bisection
        Purpose:
            Computes the root of a function f between a and b.
        Arguments:
            f (function):
                A function f: R -> R. The root of f is computed.
            a (float):
                One of the endpoints of the interval for f.
            b (float):
                The other endpoint for f.
        Output:
            root (float):
                A root of the function f between a and b.
    """
    # Tell the algorithm to stop after several iterations to avoid an
    # infinite loop. Double precision numbers have 52 bits in the mantissa,
    # so if |b - a| ~= 1, after 52 iterations of bisection we will get as
    # close as we can to the root. To allow for |b - a| to be larger, halt
    # the algorithm after at most 64 steps.
    maximum_number_of_iterations = 64
    # The maximum allowed error. This is double precision epsilon.
    epsilon = 2.220446049250313E-16
    # Evaluate f at the two endpoints to determine which is positive and
    # which is negative. We transform [a, b] to [left, right] by doing this.
    a_eval = f(a)
    b_eval = f(b)
    # Rare case, f(a) = 0. Return a, no bisection needed.
    if a_eval == 0.0:
        return a
    # Similarly, if f(b) = 0, then we have already found the root. Return b.
    if b_eval == 0.0:
        return b
    # Compare the two evaluations and set left and right accordingly.
    if a_eval < b_eval:
        # If both evaluations are negative, or if both are positive, then
        # the bisection method will not work. Return NaN.
        if b_eval < 0.0 or a_eval > 0.0:
            return (a - a) / (a - a)
        # Otherwise, since f(a) < f(b), set left = a and right = b.
        left = a
        right = b
    # In this case the function starts positive and tends to a negative.
    else:
        # Same sanity check as before. We need one evaluation to be
        # negative and one to be positive. Abort if both have the same sign.
        if a_eval < 0.0 or b_eval > 0.0:
            return (a - a) / (a - a)
        # Since f(a) > f(b), set left = b and right = a.
        left = b
        right = a
    # Start the bisection method. Compute the midpoint of a and b.
    midpoint = 0.5 * (a + b)
    # Iteratively apply the bisection method.
    for _ in range(maximum_number_of_iterations):
        # If f(x) is very small, we are close to a root and can break out
        # of this for loop. Check for this.
        f_eval = f(midpoint)
        if abs(f_eval) <= epsilon:
            break
        # Apply bisection to get a better approximation for the root. We
        # have f(left) < 0 < f(right). If f(midpoint) < 0, replace the
        # interval [left, right] with [midpoint, right]. Set left to the
        # midpoint and reset the midpoint to be closer to right.
        if f_eval < 0.0:
            left = midpoint
            midpoint = 0.5 * (midpoint + right)
        # In the other case, f(midpoint) > 0, we replace right with the
        # midpoint, changing [left, right] into [left, midpoint]. We then
        # set the midpoint to be closer to left.
        else:
            right = midpoint
            midpoint = 0.5 * (left + midpoint)
    # After n iterations, we are no more than |b - a| / 2^n away from the
    # root of the function. 1 / 2^n goes to zero very quickly, meaning the
    # convergence is very quick.
    return midpoint
# pi is somewhere between 3 and 4, and it is a root to sine.
A = 3.0
B = 4.0
# Compute pi using bisection. We should get pi = 3.14159..., accurate
# to about 16 decimals.
pi_by_bisection = bisection(math.sin, A, B)
print(f'pi = {pi_by_bisection}')
 
R
################################################################################
#                                   LICENSE                                    #
################################################################################
#   This file is part of mitx_mathematics_programming_examples.                #
#                                                                              #
#   mitx_mathematics_programming_examples is free software: you can            #
#   redistribute it and/or modify it under the terms of the GNU General Public #
#   License as published by the Free Software Foundation, either version 3 of  #
#   the License, or (at your option) any later version.                        #
#                                                                              #
#   mitx_mathematics_programming_examples is distributed in the hope that it   #
#   will be useful, but WITHOUT ANY WARRANTY; without even the implied         #
#   warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  #
#   GNU General Public License for more details.                               #
#                                                                              #
#   You should have received a copy of the GNU General Public License          #
#   along with mitx_mathematics_programming_examples.  If not, see             #
#   <https://www.gnu.org/licenses/>.                                           #
################################################################################
#   Purpose:                                                                   #
#       Calculates roots of a function using bisection.                        #
################################################################################
#   Author:     Ryan Maguire                                                   #
#   Date:       May 18, 2025.                                                  #
################################################################################
# Computes the root of a function via bisection.
bisection <- function(f, a, b) {
    # Tell the algorithm to stop after several iterations to avoid an
    # infinite loop. Double precision numbers have 52 bits in the mantissa,
    # so if |b - a| ~= 1, after 52 iterations of bisection we will get as
    # close as we can to the root. To allow for |b - a| to be larger, halt
    # the algorithm after at most 64 steps.
    maximum_number_of_iterations <- 64
    # The maximum allowed error. This is double precision epsilon.
    epsilon <- 2.220446049250313E-16
    # Evaluate f at the two endpoints to determine which is positive and
    # which is negative. We transform [a, b] to [left, right] by doing this.
    a_eval <- f(a)
    b_eval <- f(b)
    # Rare case, f(a) = 0. Return a, no bisection needed.
    if (a_eval == 0.0) {
        return(a)
    }
    # Similarly, if f(b) = 0, then we have already found the root. Return b.
    if (b_eval == 0.0) {
        return(b)
    }
    # Compare the two evaluations and set left and right accordingly.
    if (a_eval < b_eval) {
        # If both evaluations are negative, or if both are positive, then
        # the bisection method will not work. Return NaN.
        if ((b_eval < 0.0) || (a_eval > 0.0)) {
            return((a - a) / (a - a))
        }
        # Otherwise, since f(a) < f(b), set left = a and right = b.
        left <- a
        right <- b
    # In this case the function starts positive and tends to a negative.
    } else {
        # Same sanity check as before. We need one evaluation to be
        # negative and one to be positive. Abort if both have the same sign.
        if ((a_eval < 0.0) || (b_eval > 0.0)) {
            return((a - a) / (a - a))
        }
        # Since f(a) > f(b), set left = b and right = a.
        left <- b
        right <- a
    }
    # Start the bisection method. Compute the midpoint of a and b.
    midpoint <- 0.5 * (a + b)
    # Iteratively apply the bisection method.
    for (iters in 1:maximum_number_of_iterations) {
        # If f(x) is very small, we are close to a root and can break out
        # of this for loop. Check for this.
        f_eval <- f(midpoint)
        if (abs(f_eval) <= epsilon) {
            break
        }
        # Apply bisection to get a better approximation for the root. We
        # have f(left) < 0 < f(right). If f(midpoint) < 0, replace the
        # interval [left, right] with [midpoint, right]. Set left to the
        # midpoint and reset the midpoint to be closer to right.
        if (f_eval < 0.0) {
            left <- midpoint
            midpoint <- 0.5 * (midpoint + right)
        # In the other case, f(midpoint) > 0, we replace right with the
        # midpoint, changing [left, right] into [left, midpoint]. We then
        # set the midpoint to be closer to left.
        } else {
            right <- midpoint
            midpoint <- 0.5 * (left + midpoint)
        }
    }
    # After n iterations, we are no more than |b - a| / 2^n away from the
    # root of the function. 1 / 2^n goes to zero very quickly, meaning the
    # convergence is very quick.
    return(midpoint)
}
# pi is somewhere between 3 and 4, and it is a root to sine.
A <- 3.0
B <- 4.0
# Compute pi using bisection. We should get pi = 3.14159..., accurate
# to about 16 decimals.
pi_by_bisection <- bisection(sin, A, B)
output = sprintf("pi = %.16f", pi_by_bisection)
message(output)
 
Rust
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using bisection.                    *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/04/17                                                        *
 ******************************************************************************/
/*  Function pointer notation is a little confusing. Create a typedef for it  *
 *  so we do not need to explicitly use it later.                             */
type RealFunc = fn(f64) -> f64;
/*  Computes the root of a function using the bisection method.               */
fn bisection_method(f: RealFunc, a: f64, b: f64) -> f64 {
    /*  Tell the algorithm to stop after several iterations to avoid an       *
     *  infinite loop. Double precision numbers have 52 bits in the mantissa, *
     *  so if |b - a| ~= 1, after 52 iterations of bisection we will get as   *
     *  close as we can to the root. To allow for |b - a| to be larger, halt  *
     *  the algorithm after at most 64 steps.                                 */
    const MAXIMUM_NUMBER_OF_ITERATIONS: u32 = 64;
    /*  Getting exact roots is hard using floating-point numbers. Allow a     *
     *  tolerance in our computation. This value is double precision epsilon. */
    const EPSILON: f64 = 2.220446049250313E-16;
    /*  The midpoint for the bisection method. This will update as we iterate.*/
    let mut midpoint: f64;
    /*  We do not require a < b, nor do we require f(a) < f(b). We only need  *
     *  one of these to evaluate to a negative under f and one to evaluate to *
     *  positive. We will call the negative entry left and positive one right.*/
    let mut left: f64;
    let mut right: f64;
    /*  Evaluate f at the two endpoints to determine which is positive and    *
     *  which is negative. We transform [a, b] to [left, right] by doing this.*/
    let a_eval: f64 = f(a);
    let b_eval: f64 = f(b);
    /*  Rare case, f(a) = 0. Return a, no bisection needed.                   */
    if a_eval == 0.0 {
        return a;
    }
    /*  Similarly, if f(b) = 0, then we have already found the root. Return b.*/
    if b_eval == 0.0 {
        return b;
    }
    /*  Compare the two evaluations and set left and right accordingly.       */
    if a_eval < b_eval {
        /*  If both evaluations are negative, or if both are positive, then   *
         *  the bisection method will not work. Return NaN.                   */
        if b_eval < 0.0 || a_eval > 0.0 {
            return (a - a) / (a - a);
        }
        /*  Otherwise, since f(a) < f(b), set left = a and right = b.         */
        left = a;
        right = b;
    /*  In this case the function starts positive and tends to a negative.    */
    } else {
        /*  Same sanity check as before. We need one evaluation to be         *
         *  negative and one to be positive. Abort if both have the same sign.*/
        if a_eval < 0.0 || b_eval > 0.0 {
            return (a - a) / (a - a);
        }
        /*  Since f(a) > f(b), set left = b and right = a.                    */
        left = b;
        right = a;
    }
    /*  Start the bisection method. Compute the midpoint of a and b.          */
    midpoint = 0.5 * (a + b);
    /*  Iteratively divide the range in half to find the root.                */
    for _ in 0 .. MAXIMUM_NUMBER_OF_ITERATIONS {
        /*  If f(x) is very small, we are close to a root and can break out   *
         *  of this for loop. Check for this.                                 */
        let eval: f64 = f(midpoint);
        if eval.abs() <= EPSILON {
            break;
        }
        /*  Apply bisection to get a better approximation for the root. We    *
         *  have f(left) < 0 < f(right). If f(midpoint) < 0, replace the      *
         *  interval [left, right] with [midpoint, right]. Set left to the    *
         *  midpoint and reset the midpoint to be closer to right.            */
        if eval < 0.0 {
            left = midpoint;
            midpoint = 0.5 * (midpoint + right);
        /*  In the other case, f(midpoint) > 0, we replace right with the     *
         *  midpoint, changing [left, right] into [left, midpoint]. We then   *
         *  set the midpoint to be closer to left.                            */
        } else {
            right = midpoint;
            midpoint = 0.5 * (left + midpoint);
        }
    }
    /*  After n iterations, we are no more than |b - a| / 2^n away from the   *
     *  root of the function. 1 / 2^n goes to zero very quickly, meaning the  *
     *  convergence is very quick.                                            */
    return midpoint;
}
/*  End of bisection_method.                                                  */
/*  Main routine used for testing our implementation of the bisection method. */
fn main() {
    /*  pi is somewhere between 3 and 4, and it is a root to sine.            */
    const A: f64 = 3.0;
    const B: f64 = 4.0;
    /*  Compute pi using bisection. We should get pi = 3.14159..., accurate   *
     *  to about 16 decimals.                                                 */
    let pi: f64 = bisection_method(f64::sin, A, B);
    println!("pi( = {}", pi);
}
 
Swift
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using bisection.                    *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/04/17                                                        *
 ******************************************************************************/
/*  fabs (floating-point absolute value) found here.                          */
import Foundation
/*  Function pointer notation is a little confusing. Create a typedef for it  *
 *  so we do not need to explicitly use it later.                             */
typealias RealFunc = (Double) -> Double
/*  Computes the root of a function using the bisection method.               */
func bisectionMethod(f: RealFunc, a: Double, b: Double) -> Double {
    /*  Tell the algorithm to stop after several iterations to avoid an       *
     *  infinite loop. Double precision numbers have 52 bits in the mantissa, *
     *  so if |b - a| ~= 1, after 52 iterations of bisection we will get as   *
     *  close as we can to the root. To allow for |b - a| to be larger, halt  *
     *  the algorithm after at most 64 steps.                                 */
    let maximum_number_of_iterations: UInt32 = 64
    /*  Getting exact roots is hard using floating-point numbers. Allow a     *
     *  tolerance in our computation. This value is double precision epsilon. */
    let epsilon: Double = 2.220446049250313E-16
    /*  The midpoint for the bisection method. This will update as we iterate.*/
    var midpoint: Double
    /*  We do not require a < b, nor do we require f(a) < f(b). We only need  *
     *  one of these to evaluate to a negative under f and one to evaluate to *
     *  positive. We will call the negative entry left and positive one right.*/
    var left: Double
    var right: Double
    /*  Evaluate f at the two endpoints to determine which is positive and    *
     *  which is negative. We transform [a, b] to [left, right] by doing this.*/
    let a_eval: Double = f(a)
    let b_eval: Double = f(b)
    /*  Rare case, f(a) = 0. Return a, no bisection needed.                   */
    if a_eval == 0.0 {
        return a
    }
    /*  Similarly, if f(b) = 0, then we have already found the root. Return b.*/
    if b_eval == 0.0 {
        return b
    }
    /*  Compare the two evaluations and set left and right accordingly.       */
    if a_eval < b_eval {
        /*  If both evaluations are negative, or if both are positive, then   *
         *  the bisection method will not work. Return NaN.                   */
        if b_eval < 0.0 || a_eval > 0.0 {
            return (a - a) / (a - a)
        }
        /*  Otherwise, since f(a) < f(b), set left = a and right = b.         */
        left = a
        right = b
    /*  In this case the function starts positive and tends to a negative.    */
    } else {
        /*  Same sanity check as before. We need one evaluation to be         *
         *  negative and one to be positive. Abort if both have the same sign.*/
        if a_eval < 0.0 || b_eval > 0.0 {
            return (a - a) / (a - a)
        }
        /*  Since f(a) > f(b), set left = b and right = a.                    */
        left = b
        right = a
    }
    /*  Start the bisection method. Compute the midpoint of a and b.          */
    midpoint = 0.5 * (a + b)
    /*  Iteratively divide the range in half to find the root.                */
    for _ in 0 ..< maximum_number_of_iterations {
        /*  If f(x) is very small, we are close to a root and can break out   *
         *  of this for loop. Check for this.                                 */
        let eval: Double = f(midpoint)
        if abs(eval) <= epsilon {
            break
        }
        /*  Apply bisection to get a better approximation for the root. We    *
         *  have f(left) < 0 < f(right). If f(midpoint) < 0, replace the      *
         *  interval [left, right] with [midpoint, right]. Set left to the    *
         *  midpoint and reset the midpoint to be closer to right.            */
        if eval < 0.0 {
            left = midpoint
            midpoint = 0.5 * (midpoint + right)
        /*  In the other case, f(midpoint) > 0, we replace right with the     *
         *  midpoint, changing [left, right] into [left, midpoint]. We then   *
         *  set the midpoint to be closer to left.                            */
        } else {
            right = midpoint
            midpoint = 0.5 * (left + midpoint)
        }
    }
    /*  After n iterations, we are no more than |b - a| / 2^n away from the   *
     *  root of the function. 1 / 2^n goes to zero very quickly, meaning the  *
     *  convergence is very quick.                                            */
    return midpoint
}
/*  End of bisection_method.                                                  */
/*  pi is somewhere between 3 and 4, and it is a root to sine.                */
let a: Double = 3.0
let b: Double = 4.0
/*  Compute pi using bisection. We should get pi = 3.14159..., accurate       *
 *  to about 16 decimals.                                                     */
let pi: Double = bisectionMethod(f: sin, a: a, b: b)
print("pi = \(pi)")
 
TypeScript
/******************************************************************************
 *                                  LICENSE                                   *
 ******************************************************************************
 *  This file is part of mitx_mathematics_programming_examples.               *
 *                                                                            *
 *  mitx_mathematics_programming_examples is free software: you can           *
 *  redistribute it and/or modify it under the terms of the GNU General       *
 *  Public License as published by the Free Software Foundation, either       *
 *  version 3 of the License, or (at your option) any later version.          *
 *                                                                            *
 *  mitx_mathematics_programming_examples is distributed in the hope that     *
 *  it will be useful but WITHOUT ANY WARRANTY; without even the implied      *
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 *  See the GNU General Public License for more details.                      *
 *                                                                            *
 *  You should have received a copy of the GNU General Public License         *
 *  along with mitx_mathematics_programming_examples. If not, see             *
 *  <https://www.gnu.org/licenses/>.                                          *
 ******************************************************************************
 *  Purpose:                                                                  *
 *      Calculates the root of a function using bisection.                    *
 ******************************************************************************
 *  Author: Ryan Maguire                                                      *
 *  Date:   2025/05/18                                                        *
 ******************************************************************************/
/*  Type for a function of the form f: R -> R.                                */
type realfunc = (x: number) => number;
/*  Computes the root of a function using the bisection method.               */
function bisectionMethod(f: realfunc, a: number, b: number): number {
    /*  Tell the algorithm to stop after several iterations to avoid an       *
     *  infinite loop. Double precision numbers have 52 bits in the mantissa, *
     *  so if |b - a| ~= 1, after 52 iterations of bisection we will get as   *
     *  close as we can to the root. To allow for |b - a| to be larger, halt  *
     *  the algorithm after at most 64 steps.                                 */
    const MAXIMUM_NUMBER_OF_ITERATIONS: number = 64;
    /*  Getting exact roots is hard using floating-point numbers. Allow a     *
     *  tolerance in our computation. This value is double precision epsilon. */
    const EPSILON: number = 2.220446049250313E-16;
    /*  The midpoint for the bisection method. This will update as we iterate.*/
    let midpoint: number;
    /*  Variable for keeping track of the number of iterations performed.     */
    let iters: number;
    /*  We do not require a < b, nor do we require f(a) < f(b). We only need  *
     *  one of these to evaluate to a negative under f and one to evaluate to *
     *  positive. We will call the negative entry left and positive one right.*/
    let left: number;
    let right: number;
    /*  Evaluate f at the two endpoints to determine which is positive and    *
     *  which is negative. We transform [a, b] to [left, right] by doing this.*/
    const A_EVAL: number = f(a);
    const B_EVAL: number = f(b);
    /*  Rare case, f(a) = 0. Return a, no bisection needed.                   */
    if (A_EVAL == 0.0) {
        return a;
    }
    /*  Similarly, if f(b) = 0, then we have already found the root. Return b.*/
    if (B_EVAL == 0.0) {
        return b;
    }
    /*  Compare the two evaluations and set left and right accordingly.       */
    if (A_EVAL < B_EVAL) {
        /*  If both evaluations are negative, or if both are positive, then   *
         *  the bisection method will not work. Return NaN.                   */
        if ((B_EVAL < 0.0) || (A_EVAL > 0.0)) {
            return (a - a) / (a - a);
        }
        /*  Otherwise, since f(a) < f(b), set left = a and right = b.         */
        left = a;
        right = b;
    /*  In this case the function starts positive and tends to a negative.    */
    } else {
        /*  Same sanity check as before. We need one evaluation to be         *
         *  negative and one to be positive. Abort if both have the same sign.*/
        if ((A_EVAL < 0.0) || (B_EVAL > 0.0)) {
            return (a - a) / (a - a);
        }
        /*  Since f(a) > f(b), set left = b and right = a.                    */
        left = b;
        right = a;
    }
    /*  Start the bisection method. Compute the midpoint of a and b.          */
    midpoint = 0.5 * (a + b);
    /*  Iteratively divide the range in half to find the root.                */
    for (iters = 0; iters < MAXIMUM_NUMBER_OF_ITERATIONS; ++iters) {
        /*  If f(x) is very small, we are close to a root and can break out   *
         *  of this for loop. Check for this.                                 */
        const EVAL: number = f(midpoint);
        if (Math.abs(EVAL) <= EPSILON) {
            break;
        }
        /*  Apply bisection to get a better approximation for the root. We    *
         *  have f(left) < 0 < f(right). If f(midpoint) < 0, replace the      *
         *  interval [left, right] with [midpoint, right]. Set left to the    *
         *  midpoint and reset the midpoint to be closer to right.            */
        if (EVAL < 0.0) {
            left = midpoint;
            midpoint = 0.5 * (midpoint + right);
        /*  In the other case, f(midpoint) > 0, we replace right with the     *
         *  midpoint, changing [left, right] into [left, midpoint]. We then   *
         *  set the midpoint to be closer to left.                            */
        } else {
            right = midpoint;
            midpoint = 0.5 * (left + midpoint);
        }
    }
    /*  After n iterations, we are no more than |b - a| / 2^n away from the   *
     *  root of the function. 1 / 2^n goes to zero very quickly, meaning the  *
     *  convergence is very quick.                                            */
    return midpoint;
}
/*  End of bisectionMethod.                                                   */
/*  pi is somewhere between 3 and 4, and it is a root to sine.                */
const A: number = 3.0;
const B: number = 4.0;
/*  Compute pi using bisection. We should get pi = 3.14159..., accurate       *
 *  to about 16 decimals.                                                     */
const PI: number = bisectionMethod(Math.sin, A, B);
const OUTPUT: string = "pi = " + PI.toFixed(16);
console.log(OUTPUT);