add precomputed mathematical constants and specialize the second division residual
.NET Test / .NET tests (push) Successful in 2m23s
.NET Test / .NET tests (push) Successful in 2m23s
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
"""Print BasicArithmeticMatchesPrecomputedIrrationalResults InlineData.
|
||||
|
||||
Run with Python 3.11+; standard library only. Decimal supplies irrational
|
||||
constants; Fraction supplies exact arithmetic on their stored binary64 pairs.
|
||||
The test suite consumes the literals, not this script or Python at runtime.
|
||||
"""
|
||||
|
||||
from decimal import Decimal, localcontext
|
||||
from fractions import Fraction
|
||||
|
||||
|
||||
def arctan_inverse(inverse: int) -> Decimal:
|
||||
"""Evaluate atan(1/inverse) using its alternating power series."""
|
||||
x = Decimal(1) / inverse
|
||||
power = x
|
||||
total = x
|
||||
index = 1
|
||||
while True:
|
||||
power *= -(x * x)
|
||||
updated = total + power / (2 * index + 1)
|
||||
if updated == total:
|
||||
return total
|
||||
total = updated
|
||||
index += 1
|
||||
|
||||
|
||||
def split(value: Fraction) -> tuple[float, float]:
|
||||
"""Round high, then the exact residual, to nearest-even binary64."""
|
||||
high = float(value)
|
||||
return high, float(value - Fraction(high))
|
||||
|
||||
|
||||
def fixtures(precision: int) -> list[tuple[str, list[float]]]:
|
||||
with localcontext() as context:
|
||||
context.prec = precision
|
||||
constants = {
|
||||
"pi": 16 * arctan_inverse(5) - 4 * arctan_inverse(239),
|
||||
"e": Decimal(1).exp(),
|
||||
"sqrt2": Decimal(2).sqrt(),
|
||||
"sqrt3": Decimal(3).sqrt(),
|
||||
"ln2": Decimal(2).ln(),
|
||||
"phi": (1 + Decimal(5).sqrt()) / 2,
|
||||
}
|
||||
pairs = {name: split(Fraction(value)) for name, value in constants.items()}
|
||||
|
||||
rows = []
|
||||
for left_name, right_name in [("pi", "e"), ("sqrt2", "sqrt3"), ("ln2", "phi")]:
|
||||
for overload in ["DD/DD", "DD/double", "double/DD"]:
|
||||
left_high, left_low = pairs[left_name]
|
||||
right_high, right_low = pairs[right_name]
|
||||
if overload == "DD/double":
|
||||
right_low = 0.0
|
||||
if overload == "double/DD":
|
||||
left_low = 0.0
|
||||
left = Fraction(left_high) + Fraction(left_low)
|
||||
right = Fraction(right_high) + Fraction(right_low)
|
||||
values = [left_high, left_low, right_high, right_low]
|
||||
for expected in [left + right, left - right, left * right, left / right]:
|
||||
high, low = split(expected)
|
||||
# These precomputed references are much closer than the test's
|
||||
# 2^-100 bound; this does not assert library correct rounding.
|
||||
assert abs(Fraction(high) + Fraction(low) - expected) <= abs(expected) / (1 << 105)
|
||||
values.extend([high, low])
|
||||
rows.append((f"{left_name}, {right_name}: {overload}", values))
|
||||
return rows
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
rows = fixtures(160)
|
||||
assert rows == fixtures(240), "Increase precision: binary64 fixtures did not stabilize"
|
||||
for label, values in rows:
|
||||
print(f" // {label}")
|
||||
overload = label.split(": ")[1]
|
||||
print(f' [InlineData("{overload}",')
|
||||
for index in range(0, len(values), 2):
|
||||
suffix = ")]" if index == len(values) - 2 else ","
|
||||
print(f" {values[index]!r}, {values[index + 1]!r}{suffix}")
|
||||
Reference in New Issue
Block a user