Skip to content

Divide is quadratic in the power of 5 in the denominator: dividing by 3·0.5^100000 takes 5.2 s vs 27 ms for a similar-size 3·0.7^100000 #187

Open

Description

@matt-edmondson

What's wrong

TryDivideExactly strips factors of 5 from the denominator one at a time. Each step is a full-width BigInteger modulo and division:

int fives = 0;
while ((remaining % 5).IsZero)
{
remaining /= 5;
fives++;
}

int fives = 0;
while ((remaining % 5).IsZero)
{
    remaining /= 5;
    fives++;
}

For a denominator containing 5^b with n digits, that is O(b·n) work. The loop always runs to completion, even when the quotient turns out not to terminate. Any binary fraction 2^-n (repeated halving, bisection, 0.5.Pow(n)) is stored with a significand of 5^n, so ordinary values hit this path.

Repro (net10.0, HEAD 8c6f112)

var inv = CultureInfo.InvariantCulture;
var half = PreciseNumber.Parse("0.5", inv).Pow(PreciseNumber.Parse("100000", inv));
PreciseNumber.Divide(PreciseNumber.One, half * PreciseNumber.Parse("3", inv), 10);
n 1 / (3·0.5^n) 1 / (0.5^n·1.3) 1 / (3·0.7^n) (more digits)
20 000 257 ms 250 ms 3 ms
50 000 1.27 s 1.24 s 9 ms
100 000 5.17 s 6.6 s 27 ms

Why it matters

Divide and operator / sit underneath almost every transcendental function. A 10-digit answer should cost roughly the same however many factors of 5 the denominator has. Instead the cost grows with the square of the operand size, for exactly the values that binary-fraction workloads produce.

Suggested fix / acceptance criteria

  • Don't count the fives one at a time.
    • If the quotient terminates, remaining must be exactly 5^b, with b ≈ bitLength / log2(5). Compute that candidate with BigInteger.Pow(5, b) (checking b±1) and compare.
    • Or remove fives by repeated squaring or binary search on 5^k, the way CountTrailingZeros handles tens.
  • Add a benchmark or timed test: Divide(1, 3·0.5^100000, 10) finishes in roughly the same time as Divide(1, 3·0.7^100000, 10), and exact quotients like 1 / 0.5^n still come back exact.

Activity

  1. matt-edmondson commented on Nov 2, 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

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions