@@ -0,0 +1,63 @@
|
||||
"""Regenerate Exp reference rows using only Python's standard library.
|
||||
|
||||
Decimal.exp/ln are correctly rounded. Evaluate exact stored binary64 sums at
|
||||
180 and 260 digits and require identical 120-digit references and split inputs.
|
||||
No DD implementation, binary64 exp, or DD-rounded mathematical constants are used.
|
||||
Run from the repo root; stdout is the body of ExpReferenceData.Cases.
|
||||
"""
|
||||
from decimal import Decimal, localcontext
|
||||
import math
|
||||
import random
|
||||
|
||||
|
||||
def generate(precision):
|
||||
with localcontext() as context:
|
||||
context.prec = precision
|
||||
two = Decimal(2)
|
||||
ln2 = two.ln()
|
||||
high_ln2 = float(ln2)
|
||||
low_ln2 = float(ln2 - Decimal.from_float(high_ln2))
|
||||
tail_ln2 = float(ln2 - Decimal.from_float(high_ln2) - Decimal.from_float(low_ln2))
|
||||
inputs = [(x, 0.0) for x in [1.0, -1.0, 0.5, -0.5, 709.5, 709.781, -740.0,
|
||||
-745.131, 710.0, -746.0, 1e-30, -1e-30, 5e-324, -5e-324]]
|
||||
for high in [1.0, -1.0, 0.5, -0.5, 709.5, -740.0]:
|
||||
for low in [math.ulp(high) / 4, -math.ulp(high) / 4, 5e-324, -5e-324]:
|
||||
inputs.append((high, low))
|
||||
rng = random.Random(271828)
|
||||
for _ in range(240):
|
||||
high = rng.uniform(-745, 709.78)
|
||||
inputs.append((high, rng.choice([-1, 0, 1]) * math.ulp(high) / 4))
|
||||
# Neighbors of binary range-reduction half steps, covering both signs of k.
|
||||
for k in [-1074, -1022, -100, -1, 0, 1, 100, 1023]:
|
||||
midpoint = (Decimal(k) + Decimal('0.5')) * ln2
|
||||
high = float(midpoint)
|
||||
low = float(midpoint - Decimal.from_float(high))
|
||||
inputs.extend((high, residual) for residual in [math.nextafter(low, -math.inf), low, math.nextafter(low, math.inf)])
|
||||
# Adjacent representable low components straddling true overflow and
|
||||
# half-minimum-subnormal thresholds. Min-normal neighbors pin gradual underflow.
|
||||
for boundary in [(two ** 1024 - two ** 970).ln(), -1075 * ln2, -1022 * ln2]:
|
||||
high = float(boundary)
|
||||
low = float(boundary - Decimal.from_float(high))
|
||||
inputs.extend((high, residual) for residual in [math.nextafter(low, -math.inf), low, math.nextafter(low, math.inf)])
|
||||
rows = []
|
||||
for high, low in inputs:
|
||||
# 1100 digits preserve even a minimum-subnormal low beside these
|
||||
# bounded highs. Decimal.exp consumes this exact input before rounding.
|
||||
with localcontext() as input_context:
|
||||
input_context.prec = 1100
|
||||
exact = Decimal.from_float(high) + Decimal.from_float(low)
|
||||
result = exact.exp()
|
||||
overflow = result >= two ** 1024 - two ** 970
|
||||
underflow = result <= two ** -1075
|
||||
with localcontext() as output_context:
|
||||
output_context.prec = 120
|
||||
reference = format(+result, 'e')
|
||||
rows.append(f' yield return new({high!r}, {low!r}, "{reference}", {str(overflow).lower()}, {str(underflow).lower()});')
|
||||
return tail_ln2, rows
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
first = generate(180)
|
||||
assert first == generate(260), 'References changed with increased precision'
|
||||
print(f'// ln(2) third component: {first[0]!r}')
|
||||
print('\n'.join(first[1]))
|
||||
Reference in New Issue
Block a user