fix(cci,correl,willr): divide by the value the guard tests, and make %R's range a guarantee (#395) - #397
Merged
Conversation
…%R's range a guarantee (#395) The three remaining functions carrying #390's shape: a guard that establishes one expression while the arithmetic below it divides by another. All three are reachable through the shipped batch API and all three return a wrong answer under TA_SUCCESS. CCI guards `tempReal2` and divided by `0.015*tempReal2`. The band is RELATIVE and the underflow is ABSOLUTE, so they cross: below |average| ~ 1.6e-308 the guard still answers "not flat" while the scaled copy is exactly 0.0. Dividing by the deviation and scaling after removes the mismatch structurally. It also fixes the silent half, which is larger than the loud one -- the pre-scaled form loses precision long before it reaches Inf, measured relative error 7.7e-08 at price scale 1e-314 rising to 4.8e-01 at 1e-321, against 2e-16 for the new form at every one of those magnitudes. CORREL guards ssX and ssY separately -- correctly, and correl.c explains why -- and divided by `sqrt(ssX*ssY)`. The product carries the fourth power of the window spread, so it leaves the double range at both ends while both factors are still ordinary normals with all 53 bits, and no test of the factors can see it. Where spXY is zero the result is NaN, which the [-1,1] clamp does not catch (`NaN > 1.0` is false); where it is not, the +-Inf is rewritten to exactly +-1.0, a perfect correlation reported from a degenerate window. A root of each factor cannot do either: sqrt maps any finite positive double into [2.3e-162, 1.4e154], so their product is representable at both ends. That also deletes the comment block arguing for the one-root form, whose overflow reasoning the tree's own oracle (ta_test_reference.c) already contradicted. WILLR is two defects, and the maintainer's ruling on the second is on #395. It still carried the exact `diff != 0.0` that #107 replaced in STOCH, so a machine-flat window divided sub-epsilon residue into full-scale noise, and a denormal range underflowed `diff` to 0.0 and answered 0 -- the value meaning a close at the period HIGH -- for a close sitting on the period LOW. Separately, the pre-scaled divisor cost the endpoint exactness that keeps %R in its documented range: the shipped library returns -100.00000000000001 on the reference series at periods 2, 3 and 4. Dividing by the range and scaling after makes the bound a theorem for well-formed input (fl(h-c) <= fl(h-l) by monotonicity, so the quotient is in [0,1] under every rounding mode); the clamp extends it to input TA-Lib does not validate, a close outside its own bar. `diff` leaves the body and the streaming handle of all four backends. Not scoped here, and checked rather than assumed: an exhaustive scan of every division in ta_codegen/input finds no fourth instance of the shape. The Hilbert family matches it lexically and is cleared -- `atan(Im/Re)*rad2Deg` can only reach +-Inf, which the `[0.67*prev, 1.5*prev]` then `[6,50]` clamp absorbs on the next statement -- so #396's seven rows are rewritten from "reachability unproven" to that clearance. The sweep from #396 now has no live instance to point at, so its self-test reconstructs CCI's pre-#395 divisor instead of reading a shipped defect, and pins the third half the other arms pin: silent when nothing bounds an operand. Two soundness fixes on the arm: its whole-divisor test accepted any operator, and its equality arm lacked the exclusivity its siblings carry. CORREL's fixed divisor is annotated rather than special-cased, which makes reverting correl.c:236 fail twice -- the key goes stale and the new divisor arrives unannotated. Measured, not assumed. LEGACY_TOL instrumented to record every function in one run: CCI 1.42e-14 and WILLR 7.11e-15 are new rows at 5e-14 and 3e-14; CORREL's 3.15e-13 is unchanged, so its #242 row stands. --fuzz-064 gives WILLR its own 5e-14 absolute bound rather than leaving it in the FMA bucket, whose 1e-9 is five orders looser than it needs -- the reason #338 gives for naming ATR. Absolute because %R is a bounded dimensionless oscillator: its floor is a ULP of 100 at any input magnitude. Six new legs, each sabotage-proven against a build of the pre-fix arithmetic: CCI subnormal (was +-Inf, now exactly +-1/0.015 -- period 2 makes the mean deviation equal the numerator, so the expectation is an equality); CORREL at 2^-283 (was NaN, -1, +1; now 0 and +-1/sqrt(15)); and WILLR's endpoint, range, denormal window, clamp domain (was +-10000) and machine-flat window. Green: full C suite, generator cargo test and clippy -D warnings, Rust clippy/doc/doctests/tests, --codegen against the frozen oracle (161 passed 0 failed in each of the four backends, CCI and WILLR both value-compared), --xlang-hash (338,957 golden cases, four backends bit-identical), --fuzz-064 (0 failures), regen-check, and the four source/lock/retcode/candle checks. Shipped-build A/B on a Zen 4 laptop, min of 12 alternating rounds x 200 reps, 20k bars, four untouched controls inside +-2%: WILLR batch 0.917 (the batch tier LOSES a division), WILLR streaming 1.054, CCI 1.115, CORREL 1.142. The streaming +5% is the ALT1 tier paying per bar for a hoist it used to amortise, the same order as KAMA's clamp in #390. CCI's and CORREL's costs are one extra divide and one extra sqrt per bar; both were checked for a cheaper correct form and neither has one that does not reintroduce an overflow band or retighten a guard #253 deliberately widened. CORREL's is well under the ~25% the deleted comment claimed. Closes #395. Claude-Session: https://claude.ai/code/session_01GesMuivQUwX4xHr4hEtnS8
# Conflicts: # ta_codegen/output/java/library/src/main/java/io/github/talib/BuildStamp.java # ta_codegen/output/java/tools/TaCodegenServe.java
This was referenced Sep 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #395. The three functions left carrying #390's shape — a guard that establishes one expression while the arithmetic below it divides by another. All three are reachable through the shipped batch API and all three return a wrong answer under
TA_SUCCESS.tempReal20.015*tempReal2±Inf, and silently imprecise well before thatssX,ssYseparatelysqrt(ssX*ssY)NaN, or±1.0from a degenerate windowdiff(exact!= 0.0)diff = (h-l)/-1000for a close on the period low; values outside[-100, 0]Each now divides by the value its guard tests:
(tempReal/tempReal2)/0.015,spXY/(sqrt(ssX)*sqrt(ssY)), and((h-c)/(h-l))*(-100.0)under the scale-relative band #253 established.WILLR also gets the clamp ruled on in #395. The reformulation alone makes
[-100, 0]a theorem for well-formed input —fl(h-c) <= fl(h-l)by monotonicity, so the quotient is in[0,1]under every rounding mode. The clamp extends it to input TA-Lib does not validate: a close outside its own bar.willr.md's bound claims stay as written and become true; its formula line is corrected to the divide-first form.diffleaves the body and the streaming handle of all four backends.No fourth instance
An exhaustive scan of every division in
ta_codegen/inputfinds none. The Hilbert family matches the shape lexically and is cleared:atan(Im/Re)*rad2Degcan only reach±Inf, absorbed by the[0.67·prev, 1.5·prev]then[6,50]clamp on the next statement. #396's seven rows are rewritten from "reachability unproven" to that clearance.The sweep (on top of #396)
With no live instance left to point at,
the_inline_arm_*reconstructs CCI's pre-#395 divisor rather than reading a shipped defect, and pins the third half the other arms pin — silent when nothing bounds an operand. Two soundness fixes on the arm: its whole-divisor test accepted any operator, and its equality arm lacked the exclusivity its siblings carry. CORREL's fixed divisor is annotated rather than special-cased, so revertingcorrel.c:236fails twice — the key goes stale and the new divisor arrives unannotated.Measured, not assumed
LEGACY_TOLinstrumented to record every function in one run: CCI 1.42e-14 and WILLR 7.11e-15 are new rows at5e-14and3e-14; CORREL's 3.15e-13 is unchanged, so its #242 row stands.--fuzz-064gives WILLR its own5e-14absolute bound (measured 1.42e-14) rather than leaving it in the FMA bucket, whose1e-9is five orders looser than it needs — the reason #338 gives for naming ATR.Six new legs, each sabotage-proven against a build of the pre-fix arithmetic: CCI subnormal (
±Inf→ exactly±1/0.015), CORREL at2^-283(NaN, -1, +1→0, ∓1/sqrt(15)), and WILLR's endpoint, range, denormal window, clamp domain (±10000) and machine-flat window.Green
Full C suite · generator
cargo test+clippy -D warnings· Rust clippy/doc/doctests/tests ·--codegenagainst the frozen oracle (161 passed, 0 failed in each of the four backends; CCI and WILLR both value-compared) ·--xlang-hash(338,957 golden cases, four backends bit-identical) ·--fuzz-064(0 failures) ·regen-check· source-list/cargo-lock/stream-retcode/candle checks.Performance
Shipped-build A/B on a Zen 4 laptop, min of 12 alternating rounds × 200 reps, 20k bars, four untouched controls inside ±2%:
The streaming +5% is the ALT1 tier paying per bar for a hoist it used to amortise — the same order as KAMA's clamp in #390. CCI's and CORREL's costs are one extra divide and one extra
sqrtper bar. Both were checked for a cheaper correct form; neither has one that does not reintroduce an overflow band or retighten a guard #253 deliberately widened. CORREL's is well under the ~25% the deleted comment claimed.Builds on #396 (@kevinlincg), whose inline-divisor arm is what identifies the CCI and CORREL sites structurally.
https://claude.ai/code/session_01GesMuivQUwX4xHr4hEtnS8