def find_zeroes_2(f, a, b, epsilon=1e-10):
    """
    A formulation of the recursive version of find_zeroes that does not do explicit recursion on the stack.

    :param f: The function.
    :param a: The lower bound of the interval.
    :param b: The upper bound of the interval.
    :param epsilon: The stopping tolerance.  When the interval is less that this, the function will return.  Defaults
                    to 1e-10.
    :return: The zero-crossing of f or None if there is no zero-crossing in the interval.
    """

    # Get the bounds, as before
    low = min(a, b)
    high = max(a, b)
    middle = (a + b) / 2

    # Get the function evaluations, as before
    f_low = f(low)
    f_middle = f(middle)
    f_high = f(high)

    while abs(high - low) > epsilon:
        # If there's no zero-crossing in the interval, then we're done.
        if f_low * f_high > 0:
            return None

        if f_low * f_middle > 0:
            low = middle
            f_low = f_middle
        else:
            high = middle
            f_high = f_middle

        middle = (low + high) / 2
        f_middle = f(middle)

    return middle