Implement IHyperbolicFunctions<PreciseNumber> [minor] - #94
Merged
matt-edmondson merged 3 commits intoSep 23, 2026
Merged
Conversation
Completes the generic-math interface set tracked by #78. Sinh, Cosh, Tanh, Asinh, Acosh and Atanh, each with the (value, int significantDigits) overload the rest of the file establishes, and none of them routing through double. These are the exponentials and logarithms under other names, so they add no transcendental machinery of their own beyond one series. Which of them needs its textbook form rearranged is narrower than floating-point habit suggests, and that was measured rather than assumed: add, subtract and multiply are exact here, so cancelling two nearly equal values costs nothing by itself. Digits are lost only by cancelling against something Exp, Log, Sqrt or Divide has already rounded to a working width. Three fail that test and are rearranged. sinh sums its own series below one half instead of taking (e^x - e^-x)/2; asinh and atanh subtract their one analytically and hand the remainder to LogP1. Substituting the textbook form back in returns about thirty correct digits from a fifty-digit type at 1e-30, so the three near-zero tests fail outright on them rather than drifting. Three pass it as written. cosh sums two positive terms and has nothing to cancel. tanh is still -t/(2 + t) with t = expm1(-2x), but for range rather than precision: the negative exponent decays instead of growing, so it saturates to +/-1 where e^2x would overflow, which is what keeps Tanh(1e15) from throwing. acosh still factors the difference of squares, to keep a 2n-digit intermediate out of the root. Both are documented as such rather than as precision fixes, and the two tests that look like they police them say plainly that they do not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQLCwRG4ViVx3uEQ23aVMW
MSTEST0037, on the new hyperbolic tests. Assert.IsTrue(a >= b) reports only "expected true, got false" when it fails; the comparison asserts report both operands. The repo already uses them in the root, constant and value-type tests, so this was the odd one out rather than a new convention. Sonar flagged one of the three sites. Fixing only that one would have left the same pattern inconsistent within one file, so all three move: the tolerance check in AssertAgreesTo, the strict-range check on Tanh(20), and the precision check the analyzer named. Argument order on these is (bound, value), which is easy to invert into an assertion that passes for the wrong reason, so each was re-checked by inducing a failure it had to catch: comparing Sinh against Math.Cosh, asking Tanh(20) to be below zero, and demanding 500 more digits than Sinh returns. All three failed as they should. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQLCwRG4ViVx3uEQ23aVMW
…78-hyperbolic-functions
|
matt-edmondson
deleted the
claude/precisenumber-78-hyperbolic-functions
branch
September 23, 2026 00:03
This was referenced Sep 23, 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.



Fixes #78
IHyperbolicFunctionswas the one piece of #78 still outstanding — the issue called it "cheap once exp/log land", and #80, #81 and #82 have all since closed.Sinh,Cosh,Tanh,Asinh,AcoshandAtanh, each with the(value, int significantDigits)overload the rest of the file establishes, none routing throughdouble. That closes the set the issue opened:PreciseNumbernow satisfies every generic-math interface it listed as missing.The part worth reviewing
These are the exponentials and logarithms under other names, so almost nothing here is new machinery — one series, and otherwise
Exp,ExpM1,LogP1andSqrt. The interesting question was which of the six need their textbook form rearranged, and the answer is narrower than floating-point habit suggests.Add, subtract and multiply are exact on this type. So a difference of two nearly equal values loses nothing by itself. Digits are lost only by cancelling against a value that
Exp,Log,SqrtorDividehas already rounded to a working width.Three fail that test, and are rearranged:
sinh(e^x - e^-x)/2xasinhln(x + √(x² + 1))atanh½ ln((1 + x)/(1 - x))So
sinhsums its own series below one half, and both inverses subtract their one analytically and hand the remainder toLogP1.Three pass it as written, and are not rearranged for precision.
coshsums two positive terms and has nothing to cancel at all.tanhis still written-t/(2 + t)witht = expm1(-2x)— but for range, not precision: the negative exponent decays instead of growing, so it saturates to±1wheree^2xwould overflow.acoshstill factors the difference of squares — but to keep a2n-digit intermediate out of the root, not to save digits.I flag this because I originally wrote it up the other way round, claiming all five non-
coshforms were precision fixes. Reverting each one to its textbook form to check disproved two of them, and the code comments, the tests andCLAUDE.mdnow say so explicitly rather than carrying a tidier story that isn't true.Confirmed the tests depend on the change
Substituted the textbook form back in, one function at a time, and re-ran. 8 of 26 fail, and the three near-zero assertions fail outright rather than drifting in the last place — at
1e-30the textbook forms return about thirty correct digits from a fifty-digit type:The other two near-zero tests —
TestTanhKeepsItsDigitsNearZeroandTestAcoshKeepsItsDigitsJustAboveOne— passed on the textbook forms. They're kept as ordinary regression assertions, and both their doc comments and the class remark now say they don't police those two functions, so nobody later mistakes them for a guard they aren't.Two tests I had to correct rather than satisfy
Both were my assertions over-reaching, not implementation defects, and both are now documented at the test:
Acosh(Cosh(x))atx = 1e-30.coshis flat at zero:cosh(1e-30) - 1 = 5e-61, below any working precision short of 61 digits. Oncecoshhas rounded to exactly one, noacoshcan recover the argument. The round trip now skips the sub-0.25entries.cosh²x - sinh²x = 1atx = 100. Both are ~1.34e43, so their squares are ~1.8e86and a difference of one first appears at the 87th significant digit. At 50 digits the identity is correctly zero. Now checked at 140.Tests
Full suite green: 376 total, 0 failed (350 before, 26 added). Release build clean, zero warnings, all four TFMs.
Reference digits were computed at 80–120 digits in Python's
decimal— an independent implementation — from each function's closed form (asinh 1 = ln(1 + √2),acosh 2 = ln(2 + √3),atanh ½ = ½ ln 3), and rounded half-away-from-zero to matchReduceSignificancerather than truncated.Also covered:
cosh² - sinh² = 1andtanh = sinh/coshacross a sweep straddling the series boundary, the three round trips, oddness and evenness, exactness at zero, domain rejections (Acoshbelow one,Atanhat and beyond±1), precision rejection below one digit, and agreement withMath.Sinh/Cosh/Tanh/Asinh/Acosh/Atanhto 15 digits as the cheap regression net.Also
HyperbolicBenchmarkson the repo'sDigitsaxis (8/30/200) with allocation reported, per the issue's acceptance. Dry-run to confirm every operand is in domain; not measured for real here, since a shared CI container isn't a number worth recording.CLAUDE.mdgains the design-pattern entry and the test-structure line.Not covered
Sinh's series boundary is pinned by the identity holding on both sides of it, not by a test asserting which path ran — so a change that moved or removedDirectSeriesLimitwould be caught only if it moved far enough to cost digits at1e-30.🤖 Generated with Claude Code
https://claude.ai/code/session_01TQLCwRG4ViVx3uEQ23aVMW
Generated by Claude Code