ExactArray and Enclosure#

The rounded answer is the best the output format can hold, but it is not the exact value. When you need the exact value itself, for example to grade an implementation that is more precise than the output format or to measure an error exactly, use r.exact.

r.exact is an ExactArray: certified enclosures of the exact answer, one per element (a pair per complex element), stored packed rather than as one Python object each. Each element is a midpoint with a 128-bit significand and a radius of at most one unit in its last place, 0 when the midpoint is the exact value.

Stored magnitudes lie between 2**-(2**20) and 2**(2**20). A value beyond them is stored as an infinity (too big) or a zero (too small) with its sign and out_of_range set, which still decides its rounding to every supported format.

class ExactArray#

Certified exact values of one output, in packed storage (see the module docstring).

value(index=())#

The enclosure of one element; a (real, imaginary) pair for complex outputs.

values() -> list#

Every element's enclosure in row-major order.

reshape(shape) -> ExactArray#

The same elements in row-major order with a new shape of the same size.

take(positions, shape) -> ExactArray#

The elements at row-major positions, as an array of shape; a negative position counts from the end, as in torch.take.

paired(shape) -> ExactArray#

A real array's entries read in pairs (real part, imaginary part) as a complex array of shape.

class Enclosure(midpoint: FloatValue, radius: Fraction, out_of_range: bool = False)#

A certified value: the true value lies in [midpoint - radius, midpoint + radius].

radius is 0 when midpoint is the exact value, NaN or infinite. With out_of_range, the true value is finite and nonzero with midpoint's sign, and its magnitude is at least 2**LIMIT (midpoint infinite) or below 2**-LIMIT (midpoint zero).

Examples#

Code
import exactref as er

# The float32 values nearest 0.1, 0.2 and 0.3
x = er.asarray([0.1, 0.2, 0.3], "float32")
# Their dot product with [1, 3, 7]: a matmul of two vectors has one element
r = er.matmul(x, er.asarray([1.0, 3.0, 7.0], "float32"))

# The enclosure of that element
e = r.exact.value()
# The midpoint as an exact fraction
print("midpoint:", repr(e.midpoint.to_fraction()))
# A radius of 0 means the midpoint is the exact value
print("radius:", repr(e.radius))
# out_of_range is True only for a value too big or too small to store
print("out of range:", e.out_of_range)

# Two complex64 arrays of one element each
u, w = er.asarray([1 + 2j], "complex64"), er.asarray([3 - 1j], "complex64")
# Their elementwise product: (1 + 2j) * (3 - 1j) is 5 + 5j
z = er.multiply(u, w)
# A complex element's enclosure is a (real, imaginary) pair
real, imag = z.exact.value((0,))
print("real midpoint:", repr(real.midpoint.to_fraction()))
print("imag midpoint:", repr(imag.midpoint.to_fraction()))
Output
midpoint: Fraction(375809651, 134217728)
radius: Fraction(0, 1)
out of range: False
real midpoint: Fraction(5, 1)
imag midpoint: Fraction(5, 1)

Reading complicated tolerances uses enclosures to measure an error exactly.