import math


def exp_tuple(x, base = 10, normalize = False):
    """
    For a number x and base b, return (m, n, b) where m = x / b^n, n = int(log_b(|x|)).
    If normalize is True, the output will be tuned to ensure 1 <= |m| < b.

    (0, 0, base) will be the output for x = 0, since 0 * base^0 = 0 * 1 = 0.

    If x is ±inf or NaN it will return (x, x, base) rather than raise an exception.
    However, if b <= 1, a ValueError will be raised.
    """

    if base <= 1:
        raise ValueError("Base must be greater than 1.")
    elif x == 0:
        return 0, 0, base
    else:
        # log_b |x| is equal to n + t, where n is the integer part and t the fractional part.
        # |x| is then equal to b^(n+t) which is also b^t*b^n, known as the exponentional
        # form of |x|. Let b^t be called |m|, which is also |x|/b^n. Then x = x/b^n * b^n.
        #
        # This form is called "normalized" if 1 <= |m| < b, which means 0 <= t < 1, and
        # occurs naturally for |x| >= 1.
        try:
        # If x is ±inf or NaN return x, x, base
            int(x)
        except (OverflowError, ValueError):
            return x, x, base

        x_log = math.log(abs(x), base)
        # int() always truncates towards zero, so negative exponents are covered.
        n = int(x_log)
        t = x_log - n

    if normalize and t < 0:
        # If 0 <= t < 1, do nothing. If t >= 0, it will be < 1 by definition.
        # If t < 0, subtract 1 from n, which implicitly adds 1 to t and guarantees
        # it to be between 0 and 1. This has the effect of multiplying the original m by b.
        n -= 1

    if n == 0:
        # If 0 < |x| < base, n is zero, so m is just x.
        m = x
    else:
        # Conditional added here to mitigate rounding errors observed during testing.
        m = x / pow(base, n) if n >= 1 else x * pow(base, -n)

    return m, n, base