Skip to content

Tan refuses precisions that Sin/Cos accept: its 10 internal guard digits are counted against the π reduction ceiling #125

Description

@matt-edmondson

What's wrong

Tan(x, significantDigits) (PreciseNumber/PreciseNumber.Trigonometry.cs:234-240) calls the public SinCos(x, significantDigits + TrigonometricGuardDigits). That method runs RequireReducibleArgument(significantDigits, argumentDigits) (line 199) on the widened value.

The guard exists to limit only the digits the answer carries plus the integer digits the reduction consumes. The RequireReducibleArgument remarks, CLAUDE.md and PR #97 all say guard margin must not be limited ("margin is allowed to be unavailable"). Because Tan goes through the checked public SinCos, its 10 guard digits are counted against the ceiling. As a result, Tan refuses every request from ConstantPrecision - argDigits - 9 up to the real ceiling. That is 10 digits that Sin/Cos deliver correctly.

The parameterless Tan(x) is hit hardest. It throws for any argument with at least 140 significant digits, for example Tan(PreciseNumber.Pi / 4). Its docs don't mention this exception.

Reproduction

Sin(1, 145) → 145 digits
Tan(1, 145) → ArgumentOutOfRangeException: Reducing an argument of 1 integer digits to 155 significant digits needs π to 156 digits …
Tan(1, 140) → ArgumentOutOfRangeException (… to 150 significant digits …)
Tan(1, 139) → ok
Tan(0.3, 145) → ArgumentOutOfRangeException (0 integer digits to 155 …)
Tan(Pi/4)   → ArgumentOutOfRangeException (… to 161 significant digits …)

The digits are actually available. var (s, c) = SinCos(x, 149); Divide(s, c, 145) matched an independent 220-digit Python decimal reference in all 145 digits for x = 1, 2 and 0.3.

Suggested fix

  • In Tan, call RequireReducibleArgument(significantDigits, IntegerDigitCount(x)) against the requested precision.
  • Compute sin and cos at the widened working precision through an unchecked private core of SinCos. To do this, split SinCos into a checked public wrapper and an unchecked body.
  • Add Tan to the existing …DeliversEveryDigitUpToTheReductionCeiling test.

Related: the default-precision refusal on the constants, filed separately. That one also affects Sin/Cos; this one is specific to Tan.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingreadyFully specified; implement as written

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions