Implement ITrigonometricFunctions<PreciseNumber>, plus Atan2 [minor] - #93
Conversation
Adds the circular trigonometric functions and their inverses, none routing through double, completing #78's fourth interface surface. Sin/Cos/SinCos reduce modulo π/2 into [-π/4, π/4] with an octant index, so one halving-and-doubling kernel serves both; the reduction reads PiTo at the width the argument demands (per #79) rather than the capped Pi property, which is what keeps Sin of a large angle meaningful. Tan is sin/cos from one reduction. Atan reduces by the half-angle identity until the argument is small, then Taylor; Asin/Acos build on Atan and Sqrt, with the ±1 endpoints special-cased around the division by zero. The half-turn family reduces on the argument before multiplying by π, so SinPi(1e20) is well-defined where Sin(1e20·π) is not. Atan2 is a bespoke static rather than an interface member: it lives on IFloatingPointIeee754<T>, which PreciseNumber does not implement because it has no NaN or infinity to give the interface's edge cases meaning. Tests pin published digits for the standard angles, the Pythagorean and double-angle identities across a sweep, the SinCos/Sin+Cos agreement, the inverse round trips, Atan2 in every quadrant and on the axes, and a 120-digit Sin(1000000) reference — the last of which cannot be produced from a short π, so it fails on an implementation that reduces against one. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01By7NnPN7STCZqeAftJ1BmH
CI statusEverything on The one failure is Two things put it outside this PR:
Correction: this is transient, not an exhausted monthly quotaAn earlier version of this comment said the quota was spent and that the check would fail the same way across the org. That was wrong, and I'm correcting it rather than leaving it: the same Why I have not cleared itThe run is not retryable through the API — both of these return GHAS-managed runs aren't retryable that way. So the options are a maintainer re-running or dismissing it, or it simply re-evaluating on the next push to this branch. I have no code change to make here, and I won't push an empty commit to kick CI. Given it passed elsewhere minutes later, I'd expect a re-run to succeed. Keeping the PR watched until it is green and merged. Generated by Claude Code |
|



Fixes #82
Completes the fourth of #78's interface surfaces. Its three dependencies are now merged — #79 (wide, correctly-rounded π), #80 (
Sqrt), #81 (Exp/Logand the series machinery) — so this was unblocked.What's added
PreciseNumber/PreciseNumber.Trigonometry.csimplementsITrigonometricFunctions<PreciseNumber>and the bespokeAtan2, none routing throughdouble, every function with a(…, int significantDigits)overload matching the rest of the library:Sin,Cos,SinCos,TanAsin,Acos,Atan,Atan2SinPi,CosPi,SinCosPi,TanPi,AsinPi,AcosPi,AtanPiDegreesToRadians,RadiansToDegreesDesign, following the issue
Sin/Cos/SinCosreducex = q·π/2 + rintor ∈ [-π/4, π/4]with an octant indexq, so one kernel serves both. The kernel halvesrto below1/64, sums the sine and cosine series together, and doubles back with the double-angle identities. The reduction readsPiTo(working + argumentDigits + guard), not the cappedPiproperty — this is the concrete dependency on Pi carries 26 significant digits and Tau 25, fewer than MinimumDivisionPrecision, and Pi is truncated rather than rounded #79.SinCosdoes the reduction once, andSin/Cosboth read from it, so they agree exactly.Tanissin / cosfrom one reduction and a single division.Atanappliesatan x = 2·atan(x / (1 + √(1 + x²)))until the argument is small, then Taylor, scaling back by the matching power of two — so any magnitude converges (Atan(1e30) ≈ π/2).Asin/Acosbuild onAtanandSqrt, with±1special-cased around the√(1 - x²)division by zero.SinPi(1e20)is0, exactly, whereSin(1e20 · π)is undefined.Atan2is a bespoke static, not an interface member — it lives onIFloatingPointIeee754<T>, whichPreciseNumberdeliberately does not implement, per the issue's reasoning (no NaN/infinity to give the edge cases meaning). Quadrant dispatch onatan(y/x)with the axes handled explicitly.DegreesToRadians/RadiansToDegreesread π as the correctly-rounded literalPiTo, never derived from another constant.There is no NaN, so
Asin/Acosoutside[-1, 1]throwArgumentOutOfRangeException, asSqrtandLogalready do;Tan/TanPithrowDivideByZeroExceptionwhere the cosine is exactly zero.Tests
PreciseNumber.Test/PreciseNumberTrigonometryTests.cs, 20 methods, all pinning published values or mathematical identities rather than this library's own output:Sin/Cos/Tan(1),Asin(0.5) = π/6,Acos(0.5) = π/3,Atan(1) = π/4,Cos(π/6) = √3/2Tan = sin/cosacross a sweep spanning several revolutions and both signsSinCosagrees with separateSin/Cosexactly (the reason the member exists)Atanof a large argument,Atan2in all four quadrants and on every axis (includingAtan2(0,0)andAtan2(0,-1) = π)SinPi(1e20) = 0,SinPi(0.5) = 1,CosPi(1) = -1, andSinPi/CosPiagreeing with the radian formsMath.Sin/Cos/Atan/Atan2to 14 digits as a cheap regression netThe
Sin(1000000)test carries a 120-digit reference. That is the test the issue calls out as tying this work to #79: I verified it fails on a deliberately narrowed 26-digit π (differs by ~1e-21), and — notably — the Pythagorean-identity test still passes under that same narrow π, which is why the wide digit-string reference is the essential guard, not the identity alone. The reference itself is cross-validated by the 150-digit Pythagorean identity, the double-angle identitysin(2e6) = 2·sin(1e6)·cos(1e6)to 50 digits, and stability across 50/55/70/100/130-digit working precision.Also
PreciseNumber.Benchmarks/TrigonometryBenchmarks.csacross the repo's8/30/200Digitsaxis with adoublefloor, mirroring the exponential and root benchmarks (including theSinCos-vs-separate-calls and large-angle cases).Verification
dotnet build PreciseNumber.sln— clean, no warnings (this repo treats warnings as errors), all target frameworks (net10.0;net9.0;net8.0;net7.0;netstandard2.0;netstandard2.1)PreciseNumber.Test— 350/350 pass onnet10.0(this container has only the .NET 10 runtime; CI runs the full multi-target matrix)No public API is changed or removed; this is purely additive, hence
[minor].🤖 Generated with Claude Code
https://claude.ai/code/session_01By7NnPN7STCZqeAftJ1BmH
Generated by Claude Code