11.001001000011111101101010100010001000 Arithmazium
Home

tiny_powers_of_B()

The Python comments this walk toward the underflow threshold and, ultimately, to zero by factors of the radix. The result is a triple – small, smaller, smallest – which reflects how murky matters can be for tiny numbers. These are all simple powers of the radix, so their trailing digits are all zero.

# =============================================
# Tests for underflow behavior.
# =============================================
def tiny_powers_of_B(c):
    """Return tiny values, stepping toward zero by factors of radix or 2.

    Starting with a value like C = 1/B**k close to the underflow
    threshold, walk down by factors of H = min(1/B, 1/2). Compute
    less_tiny, less_tiny, and z differing by factors of H.
    Value less_tiny avoids anomalies. Value z typically falls to
    zero. The factor H protects against B=1.

    Args:
        c: 1/B**k, expected to be the constant C or comparable

    Returns:
        less_tiny: next tiniest positive value, by a factor of H
        tiny: tiniest positive value
        z: too tiny value, that underflows to zero or a pseudo-zero

    Basic 4430-4440
    """

The diagram below captures the action in binary as tiny reaches the smallest normal number. It’s the value with full precision that has the smallest representable exponent. Value z, which is half that, is exactly representable as a subnormal on an IEEE 754 system. On most other machines, z flushes to zero, terminating the loop.

Tiny powers of the radix

With subnormal numbers, tiny reaches the tiniest representable value, as z underflows to zero. It is the first inexact result returned. Can you see that decimal arithmetic compatible with IEEE 754 2019 would behave the same way, with division by 10 at each step?

Tiny powers of the radix

The termination criterion

if (tiny_delta <= z) or (z + z <= z):

detects several conditions:

[Epsilons are introduced elsewhere.
SUGGESTION: Add a verbose flag to the code, controlling extra output, such as is given here. Such additions to original Paranoia output are marked by “***”. The original code reflected the era, restricting output to 80 columns by 24 rows, enabling users to take screen shots.]

    tiny = c
    z = tiny * H
    while True:
        less_tiny = tiny
        tiny = z
        z = z * H
        # Terminate if z pins at a nonzero minimal value or
        # if z falls to an epsilon or 0.
        if tiny <= z or z + z <= z: break
    print("***Approaching underflow by powers of the radix:")
    print("***    less_tiny =  {:.7e}, tiny =  {:.7e},"
          + "z = {:.7e}".format(less_tiny, tiny, z))
    return less_tiny, tiny, z


Home