"""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]))