Skip to content

Carry Pi, Tau and E at 150 correctly rounded digits, and add Ln2 and Ln10 [minor] - #84

Merged
matt-edmondson merged 2 commits into
mainfrom
claude/nice-davinci-4dx5uz
Sep 16, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
claude/nice-davinci-4dx5uz

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #79

The three problems

  1. Fewer digits than the type's own default precision. Pi carried 26 significant digits, Tau 25 and E 41, against a MinimumDivisionPrecision of 50. Any expression mixing a constant with a quotient was capped at the constant's precision with nothing to indicate it — Pi / 3 was wrong from its 24th digit on.
  2. Pi was truncated, not rounded. π's 27th significant digit is 8, so a correctly rounded 26-digit π ends …434. The literal ended …433. Tau was rounded, so the two constants did not use the same rule as each other.
  3. Nothing pinned Tau == Pi * 2. They are independent literals that happened to agree to the shorter one's precision.

The change

Pi, Tau, E, plus new Ln2 and Ln10, are each carried at ConstantPrecision (150) significant digits, correctly rounded. 150 matches what ktsu.Semantics standardises on for the factors it derives from π, so the two libraries cannot disagree about π, and it leaves room for argument reduction, which cannot be more accurate than the constant it reduces by.

Each constant is its own literal, never computed from a sibling, so an error in one cannot propagate to the others.

Multiplication is exact, so a product involving a 150-digit constant carries at least 150 digits. PiTo(n), ETo(n), TauTo(n), Ln2To(n) and Ln10To(n) let a caller that only wants fifteen ask for fifteen; they round half away from zero and cache per requested precision. No transcendental function in the library reads Pi today, so nothing needed rewiring to the new accessors — that lands with the exp/log work the issue anticipates.

One wrinkle worth knowing: ln(10)'s 150th significant digit is a zero, which the constructor strips along with any other trailing zero, so Ln10.SignificantDigits reads 149 for the same 150-digit-accurate value. It is documented on the property and pinned by a test.

Tests

New PreciseNumber.Test/PreciseNumberConstantTests.cs checks every constant digit for digit against an independent computation, never against a second copy of the literal — Machin's formula for Pi and Tau, sum 1/k! for E, artanh series for Ln2 and Ln10, all in BigInteger fixed point with 40 guard digits. It also pins Tau as exactly Pi doubled, pins that no constant falls below MinimumDivisionPrecision, pins π/3 past the 24th digit, and covers PiTo at every precision from 1 to 150 along with the cache, the clamp, and the argument validation.

The three tests in PreciseNumberTests that compared each constant against a second copy of its own literal are superseded by these and have been removed, with a comment left in their place pointing at the new file.

Proof the tests catch the bug: with the old literals temporarily restored (new API kept so it still compiles), 8 of the 14 new tests fail, one per problem above —

failed TestPiIsRoundedRatherThanTruncated
  expected:   "31415926535897932384626434"
  actual:     "31415926535897932384626433"
failed TestTauIsExactlyPiDoubled
  expected: 6.2831853071795864769252866
  actual:   6.283185307179586476925287
failed TestConstantsCarryAtLeastMinimumDivisionPrecision
failed TestQuotientOfPiIsCorrectPastTheOldPrecision
  expected: 1.047197551196597746154214461093167628065723133125
  actual:   1.0471975511965977461542144333333333333333333333333

With the fix in place the whole suite passes: 277/277, Debug and Release, 0 warnings across all four target frameworks. Benchmarks were not re-run — this changes constant values and adds members, not the arithmetic internals they measure, and none of the benchmark classes touch Pi, Tau or E.

Compatibility

Pi changes value in its 26th significant digit and gains 124 more. Strictly more accurate, but an observable change to a public constant, so the commit is tagged [minor] rather than [patch]. Any downstream test pinning the old 26-digit literal will fail, which is the correct outcome — that literal was wrong in its last place.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NSqQkFe6PqeB4fuVt272j5


Generated by Claude Code

…Ln10 [minor]

Pi carried 26 significant digits, Tau 25 and E 41, all of them fewer than
MinimumDivisionPrecision (50), so any expression mixing a constant with a
quotient was capped at the constant's precision with nothing to indicate it.
Pi was also truncated rather than rounded: the 27th significant digit of pi is
8, so a 26 digit pi ends 434, and the literal ended 433. Nothing pinned
Tau == Pi * 2 either, and the two literals did not use the same rounding rule
as each other.

Carry all three at 150 significant digits, correctly rounded, and add Ln2 and
Ln10 at the same precision for the exp/log work. 150 matches what ktsu.Semantics
standardises on for the factors it derives from pi, so the two libraries cannot
disagree about it, and it leaves room for argument reduction, which cannot be
more accurate than the constant it reduces by. Each constant is its own literal,
never computed from a sibling, so an error in one cannot reach the others.

Multiplication is exact, so a product involving a 150 digit constant carries at
least 150 digits. PiTo(n), ETo(n), TauTo(n), Ln2To(n) and Ln10To(n) let a caller
that only wants fifteen ask for fifteen, rounded half away from zero and cached
per requested precision.

PreciseNumberConstantTests checks every constant digit for digit against an
independent series: Machin's formula for Pi and Tau, sum 1/k! for E, and
artanh series for Ln2 and Ln10. It also pins Tau as exactly Pi doubled, pins
that no constant falls below MinimumDivisionPrecision, and pins pi/3 past the
24th digit, where the old literal went wrong. The three tests in
PreciseNumberTests that compared each constant against a second copy of its own
literal are superseded by those.

Pi changes value in its 26th significant digit and gains 124 more, which is an
observable change to a public constant, hence the minor bump.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NSqQkFe6PqeB4fuVt272j5
SonarCloud's MSTEST0037 on the new constant test: the dedicated assertion
reports the bound and the actual digit count on failure, where Assert.IsTrue
only reports false.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NSqQkFe6PqeB4fuVt272j5
@sonarqubecloud

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pi carries 26 significant digits and Tau 25, fewer than MinimumDivisionPrecision, and Pi is truncated rather than rounded

2 participants