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

  1. matt-edmondson commented on Sep 27, 2026

    @matt-edmondson
    ContributorAuthor

    Triage


    Generated by Claude Code

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

Metadata

Metadata

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