11.001001000011111101101010100010001000 Arithmazium
Home

tiny_values_and_difference()

This function explores tiny values with nonzero low-order digits, and it looks at the differences between them. The loop has two layers, skipping over the inner layer as soon as the difference between tiny values fails the test

def tiny_values_and_difference(one_plus):
    """Compute the tiniest values from a starting value with nonzero
    low-order bits. This function is parallel to tiny_powers_of_B(),
    but looks for nonzero differences of tiny values, which are
    evidence of gradual underflow.

    If multiplication is too inaccurate, raise an error and return default
    values, shown in parens under Returns.

    Args:
        one_plus: 1 plus enough ulps that they shouldn't be lost when
            multiplied by a power of B
if (tiny_delta + tiny_delta) <= tiny_delta:

which says tiny_delta has taken a nonzero and non-𝛜 value. This indicates that there isome loss of information due to underflow.

While too_tiny is in the range of normal numbers, tinier scales back to tiny by 1/H exactly. The next diagram shows the situation in binary in the typical case that multiplication is accurate enough that a single unit in the last place will detect differences between two nearly similar values after scaling by 1/H.

    Returns:
        tiny: smallest well-behaved value (0)   *** why not tiny_B?
        tinier: first value to exhibit underflow (tiny_B)
        too_tiny: underflows to 0 or an epsilon value (too_tiny_B)
        u_threshold: smallest well-behaved value, if underflow degrades
            into nonzero values, or 0 if underflow flushes to 0 (tiny_B)
        tiny_delta: difference between two values caused by underflow (0)
        save_tiny1: value of tiny where tiny_delta computed (0)
        save_tiny2: recomputed tiny where tiny_delta computed (0)

    Basic 4450-4520
    """

Tiny values with nonzero ulps

If too_tiny underflows to zero, the loop ends with tiny_delta still zero. But with any form of gradual underflow, the loss of precision is detected. Depending on the design of the arithmetic and which mode of rounding is selected, the low-order bit of tinier may be 0 or 1. This explains the use of the absolute value in computing tiny_delta.

Tiny values, showing excptional cases

    d = C * one_plus
    # 1 + SAFE_ULPS_OF_ONE should be enough ulps of 1 that d > C.
    if d <= C:
        # But if not, we punt with default values.
        bad_cond(err_failure,
                 "multiplication gets too many last digits wrong.")
        pause()
        return 0, tiny_B, too_tiny_B, tiny_B, 0, 0, 0

The code sets the underflow threshold value to tiny the first time this behavior is discovered.

[TODO: Provide an example of an arithmetic with multiplication so bad that it falls back to default values.]

    else:
        tinier = d
        too_tiny = tinier * H
        u_threshold = ZERO
        tiny_delta = ZERO  # indicates no value computed yet
        save_tiny1 = ZERO
        save_tiny2 = ZERO
        while True:
            tiny = tinier
            tinier = too_tiny
            # This loop mirrors the computation in tiny_powers_of_B(),
            # except that the launch point is C*s with nonzero low-order
            # bits, versus C, which is just a power of the radix.
            if (tiny_delta + tiny_delta) <= tiny_delta:
                if ultra_verbose:
                    print("\tUnderflow loop: within if, tinier = {:.7e}"
                          .format(tinier))
                save_tiny2 = tinier * ONE_OVER_H
                tiny_delta = fabs(tiny - save_tiny2)  # may underflow to 0
                save_tiny1 = tiny
                if (u_threshold == ZERO) and (tiny != save_tiny2):
                    # Test for 0 captures first time through, in case
                    # tiny_delta underflows to 0.
                    u_threshold = tiny
            # Placing this statement after the (tiny*H)/H test above
            # emphasizes that only nonzero values of tinier qualify for
            # values degraded by underflow.
            too_tiny = too_tiny * H
            if ultra_verbose:
                print("\tUnderflow loop: too_tiny = {:.7e}".format(too_tiny))
            if (tinier <= too_tiny
                    or too_tiny + too_tiny <= too_tiny): break
        return (tiny, tinier, too_tiny, u_threshold,
                tiny_delta, save_tiny1, save_tiny2)


Home