https en wikipedia orgwikileibnizformulaforcf80 leibniz formula for pi the leibniz formula is the special case arctan1 pi 4 leibniz s formula converges extremely slowly it exhibits sublinear convergence convergence https en wikipedia orgwikileibnizformulaforcf80convergence we cannot try to prove against an interrupted uncompleted generation https en wikipedia orgwikileibnizformulaforcf80unusualbehaviour the errors can in fact be predicted but those calculations also approach infinity for accuracy our output will be a string so that we can definitely store all digits import math floatcalculatepi15 math pi true since we cannot predict errors or interrupt any infinite alternating series generation since they approach infinity or interrupt any alternating series we ll need math isclose math isclosefloatcalculatepi50 math pi true math isclosefloatcalculatepi100 math pi true since math pi contains only 16 digits here are some tests with known values calculatepi50 3 14159265358979323846264338327950288419716939937510 calculatepi80 3 14159265358979323846264338327950288419716939937510582097494459230781640628620899 variables used for the iteration process we can t compare against anything if we make a generator so we ll stick with plain return logic https en wikipedia org wiki leibniz_formula_for_ cf 80 leibniz formula for pi the leibniz formula is the special case arctan 1 pi 4 leibniz s formula converges extremely slowly it exhibits sublinear convergence convergence https en wikipedia org wiki leibniz_formula_for_ cf 80 convergence we cannot try to prove against an interrupted uncompleted generation https en wikipedia org wiki leibniz_formula_for_ cf 80 unusual_behaviour the errors can in fact be predicted but those calculations also approach infinity for accuracy our output will be a string so that we can definitely store all digits import math float calculate_pi 15 math pi true since we cannot predict errors or interrupt any infinite alternating series generation since they approach infinity or interrupt any alternating series we ll need math isclose math isclose float calculate_pi 50 math pi true math isclose float calculate_pi 100 math pi true since math pi contains only 16 digits here are some tests with known values calculate_pi 50 3 14159265358979323846264338327950288419716939937510 calculate_pi 80 3 14159265358979323846264338327950288419716939937510582097494459230781640628620899 variables used for the iteration process we can t compare against anything if we make a generator so we ll stick with plain return logic
def calculate_pi(limit: int) -> str:
    
    
    q = 1
    r = 0
    t = 1
    k = 1
    n = 3
    l = 3

    decimal = limit
    counter = 0

    result = ""

    
    
    while counter != decimal + 1:
        if 4 * q + r - t < n * t:
            result += str(n)
            if counter == 0:
                result += "."

            if decimal == counter:
                break

            counter += 1
            nr = 10 * (r - n * t)
            n = ((10 * (3 * q + r)) // t) - 10 * n
            q *= 10
            r = nr
        else:
            nr = (2 * q + r) * l
            nn = (q * (7 * k) + 2 + (r * l)) // (t * l)
            q *= k
            t *= l
            l += 2
            k += 1
            n = nn
            r = nr
    return result


def main() -> None:
    print(f"{calculate_pi(50) = }")
    import doctest

    doctest.testmod()


if __name__ == "__main__":
    main()