Annuity family: research round, 2026-11-04 ================================================= Outcome: blocked. No patch is offered. Nothing below changes product code, answers or the tracker. Application revision measured: 6073fff1. Evidence: CalcFunc-Evidence d224c5fb (read only). Baseline (current product), full bank, per function command: xtask-harness witness judge --bank full --func F --trace ../traces/base-F.jsonl PMT 222,823 / 291,978 matched IPMT 30 / 57 PPMT 9 / 33 CUMIPMT 12 / 38 CUMPRINC 232 / 586 FV 2,264 / 2,561 PV 1,146 / 1,308 Corpus (catalogue oracle captures, the only workbooks with annuity cells): mismatches PMT 1, IPMT 80, PPMT 104, CUMIPMT 67, CUMPRINC 157, RATE 3 (counts from the problem-functions classification) Findings (exact, reproducible from files in this folder) ------------------------------------------------------- F1. PMT consumer order, fv=0, type 0, rate>0 (structtest.py, einv.py; data base-PMT.jsonl). 4,802 bank groups share (rate, nper) with 8+ different pv each. Holding one free binary64 em per group, the four consumer orders were tested exactly (Fraction arithmetic): q=pv/em then q*rate .................. consistent in every sampled group rate/em then pv*k, pv*rate then /em, em/rate then pv/k .... 8-12 of 40 groups Precision of the two steps (all 4,802 groups): x87-stored quotient, x87-stored product (XX) ... 0 groups without a consistent em binary64 quotient, x87 product (current) ....... 59 groups without one binary64 both ................................. 111 groups without one So Excel stores RN53(RN64(pv/em)) then RN53(RN64(q*rate)), with em a stored binary64. em is pinned uniquely in 4,720 groups (edata.txt: rate, nper, em bits). F2. em itself is not identified. Against the 4,720 pinned values (probe_em.rs): current chain, Kahan expm1 of t = -(nper*log1p(rate)) ............ 3,258 same with x87 n*L, any exp backend (x87, CRT, portable) x any log ... 3,248-3,258 Kahan log1p (u=1+r, log(u)*r/(u-1)) in 6 orders ..................... <= 2,740 t kept extended (3 log routes, u stored or extended, 4 orders) ...... <= 3,142 fully extended x87 chains (FYL2X/FYL2XP1, F2XM1 direct or 2^y-1) .... <= 2,593 complement of growth (-(g-1)/g, 1/g-1, three precisions) ........... below 3,258 log via stored log2 times ln2 (3 precisions), log(1+r) ............... <= 3,057 Every one of the 1,462 misses is reproduced by the current Kahan expm1 at t shifted by 1 or 2 units in the last place (-2:46, -1:442, +1:845, +2:94; none needs more). Excel's em errs up to 3 ulp from the exact value and leans to larger magnitude (-1: 1,176 rows, +1: 762), so it is less accurate than the current chain, not more. Misses persist when 1+rate is exact in binary64 (27 of 95 such nper=1 rows), so a log1p routine is not the whole cause. F3. Measured consequence: applying F1 alone (x87 quotient in the discount arm) gives PMT 222,823 -> 222,961 (+168 / -30), all changes in the fv=0, type 0, rate>0 shape. The 30 losses are rows where today's binary64 quotient cancels an em error. Not landable under the zero-loss rule until em is identified. command: same judge with the one-line change, then xtask-harness witness compare --before ../traces/base-PMT.jsonl --after ../traces/xq-PMT.jsonl F4. Derived functions, 30-year loan block of the catalogue (sheet dcj9k_annuity rows 726-752): - CUMPRINC(1..12) equals the left-to-right binary64 sum of Excel's own twelve PPMT answers, and CUMIPMT equals 12*PMT - CUMPRINC (both bit for bit); the product already uses this shape. - PPMT is not PMT - IPMT: P - IPMT = PPMT in 4 of 12 periods, P - PPMT = IPMT in 8 of 12. - PPMT(.,1,.) = 0x...723 is not P_double - I_double for any binary64 P (the result would sit on the 2^-43 grid), not RN53(RN64(q*r) - RN64(pv*r)), and not any of 13 products P*(1+em) for either em consistent with PMT (those give ...720/721 and ...724-726): PPMT does not reuse PMT's em. Its relative error, about +4.6e-16 on all twelve periods, exceeds PMT's 1.7e-16. - probe_pp.rs: 9 discount routes x 5-6 consumer orders for PPMT and 3 for IPMT, by argument shape (pp-in.jsonl, 416 catalogue and bank rows). Nothing separates from chance: PPMT type 0, fv 0, middle periods match 10 of 41 for every route and order, and so does the product. IPMT is best as it stands except in shapes of 3-17 rows. What blocks a landable patch ---------------------------- PMT's em = (1+r)^-n - 1 (F2). Every derived function goes through it, so IPMT/PPMT/CUM* answers cannot be matched in the bank until it is known. The corpus cells (literal, ordinary rates) are also blocked: PPMT's own discount factor differs from PMT's em (F4), and no tested family reproduces it. Minimal Excel questions (none taken; Excel not driven) ------------------------------------------------------ Q1 (where does em's extra error enter?) For the 27 nper=1 bank rows with exact 1+rate that miss today (list from emdump.txt, column 3 = Excel em): =EXP(-LN(1+rate)) and =LN(EXP(-LN(1+rate))). If both equal excel_exp/excel_log bits, the error is in how expm1 combines them, not in its inputs. Q2 (is PMT's t = -nper*L?) Same rates, nper = 2 and 4, pv = 1 + j*2^-52 for j = 0..7. Doubling is exact, so if t = -n*L the pinned em must match Kahan(2t), Kahan(4t); a mismatch pattern unlike nper=1 shows a different exponent route. Q3 (PPMT's discount factor) =PPMT(r,1,1,pv) and =PPMT(r,1,2,pv) for 16 rates, pv=1. The exact answers are -pv and -pv/(2+r); P - I and P*v^m forms give different bits, which tells which identity Excel evaluates. Q4 (does PPMT reuse PMT?) =PPMT(r,k,n,pv) beside =PMT(r,n,pv)*POWER(1+r,k-1-n) and =PMT(r,n,pv)+pv*r (k=1) for the 30-year loan. Files (this folder) ------------------- corpus-annuity-cells.jsonl corpus mismatches (from the problem-functions run) catalogue-annuity.jsonl all 748 annuity formulas of the catalogue captures with Excel bits base-*.jsonl bank judge traces (current product) structtest.py einv.py rnd.py exact.py exact consumer-order tests and em pinning (Python, Fraction) einv-all.jsonl edata.txt pinned em per (rate, nper) group probe_em.rs probe_pp.rs research probes (built as cfg(test) modules of calc-func in a scratch clone only; not for landing) emdump.txt per-group em, product em, log1p, t, exp(t), reproducing t shifts UPDATE, same day: Excel questions asked, two steps landed on master (9ee28020) ============================================================================= This section supersedes "Outcome: blocked" above. Corrections to the text above: - F2 "a log1p routine is not the whole cause" was wrong. One double L per rate explains every nper (1,622/1,622 and 1,849/1,849), and that L is a non-correctly-rounded series. - F4 "PPMT does not reuse PMT's em" was only half right: PPMT uses exp(t), not 1+em. Commits: 08ea3d7a (PMT logarithm and quotient), 6c6cbd31 (IPMT/PPMT), both dc-j9k.12. Identified (type 0, -1/3 <= rate <= 1/2), RN = RN53(RN64(.)): L = 2s + 2s*(s^2*T), s = r/(2+r), T = 1/3 + s^2/5 + ... + s^20/23, all binary64 em_m = x87 Kahan expm1(-(m*L)); u_m = x87 exp(-(m*L)) q = RN(N/em_n) PMT = RN(q*r) IPMT = -RN(RN(RN(q*em_m) - RN(fv*u_m))*r) PPMT = RN(RN(RN(q-fv)*u_m)*r) fv 0, type 0 scores: PMT 11,706/11,712 (5% sample of the bank) and 720/720 fresh Excel; IPMT 95/95; PPMT 74/74 and 64/64 fresh Excel. Full bank, before -> after: PMT 222,823 -> 279,887 / 291,978 IPMT 30 -> 48 / 57 PPMT 9 -> 26 / 33 CUMIPMT 12 -> 12 / 38 CUMPRINC 232 -> 185 / 586 FV, PV unchanged; NPER and RATE not re-judged (they do not call the changed code). Corpus, catalogue cells, gained/lost: PMT +1/-0, IPMT +24/-2, PPMT +50/-2, CUMIPMT +7/-5, CUMPRINC +13/-6. Open, in order: 1. Type-1 factor K. X(q*K) with one binary64 K per rate holds on 458 of 459 bank rates and 20/20 fresh rates. Pinned K values: excel/b1-model.tsv + b1an.py, kpin.txt. Tried and failed: correctly rounded r/(1+r) (344/458), r/D(1+r), r*D(1/(1+r)), -em1, r*(1+em1), r*exp(-L), a/(1+a) with a = expm1(L), 2s/(1+s), and annuity-due forms on em_(n-1). 2. PMT fv numerator. Best X(X(pv+fv)+X(fv*em)) 5,600/7,491; 146 rows admit no binary64 N. N on u_n, and splitting q into pv and fv quotients, do no better (<= 5,120/7,484). 3. CUMPRINC per-period route: 56/135 type-0 singletons equal PPMT. Recurrences, closed balances and count*P less interest are all below that. 4. Rates outside -1/3..1/2 keep the old logarithm. The range is where 12 terms converge, not a measured Excel boundary. Rate 0.419 (near sqrt(2)-1) misses by 1 ulp, so a reduction may start there. Negative-rate em was only checked on 20 rates' controls and the bank. Questions: excel/questions-b1.tsv, excel/answers-b1.tsv (1,984 rows, Excel 16.0 build 20527, own instance, 0 input read-back differences). Q3 was asked. Q2 was answered from the bank without asking. Q1 and Q4 were not asked: the series made them unnecessary. Sources: A&S 4.1.27, Intel SDM x87, Excel's outputs (tier 1); fdlibm's log1p was tested from memory of its published form (tier 2) and rejected. No tier 3 or 4 source was read.