Skip to content

root(Number, unsigned) never returns for some inputs (Version: 3.4.0, develop 1d7669f) #8288

Description

@Platonenkov

Issue Description

The Newton–Raphson loop in root(Number f, unsigned d) (src/libxrpl/basics/Number.cpp#L1593-L1602) stops only when the new value equals the previous one or the one before it:

} while (r != rm1 && r != rm2);

For d >= 3, rounding can make the iterates cycle through three values. Neither condition then holds, and the loop runs forever. power(Number const&, unsigned, unsigned) calls root and hangs on the same inputs.

This happens under every mantissa scale and every rounding mode. Under RoundingMode::ToNearest it affects about 0.45% of random inputs with d from 2 to 6; under the other modes, about 0.04–0.1%.

No root2 hang was found in 3.2 million random inputs across all scales and rounding modes. Only tests call root(f, d) and power(f, n, d); ledger code uses root2 and power(f, n).

Steps to Reproduce

Add to src/tests/libxrpl/basics/Number.cpp:

TEST(NumberTest, root_cycle)
{
    {
        NumberMantissaScaleGuard const sg(MantissaRange::MantissaScale::Large330);
        (void)root(Number{298}, 4);        // never returns
        (void)power(Number{298}, 1, 4);    // never returns
    }
    {
        NumberMantissaScaleGuard const sg(MantissaRange::MantissaScale::Small);
        (void)root(Number{147}, 3);        // never returns
    }
}

More inputs with the default rounding mode:

  • LargeLegacy, Large320 and Large330: root(Number{298}, 4), root(Number{380}, 4) and root(Number{438}, 4).
  • Small: root(Number{147}, 3), root(Number{573}, 4) and root(Number{612}, 4).

Expected Result

root returns a value (about 4.154837722659222 for root(298, 4)) or throws.

Actual Result

The call does not return. These are the iterates of r for root(Number{298}, 4) under Large330, where f is scaled to 0.0298:

 1  0.422698722585301542
 2  0.415666404778574261
 3  0.4154838925963071552
 4  0.4154837722659744575
 5  0.415483772265922183
 6  0.4154837722659221832
 7  0.4154837722659221835
 8  0.415483772265922183
 9  0.4154837722659221832
10  0.4154837722659221835

The iterates under Small for root(Number{147}, 3), where f is scaled to 0.147:

 4  0.5277632087904077
 5  0.5277632087904073
 6  0.527763208790408
 7  0.5277632087904077
 8  0.5277632087904073
 9  0.527763208790408

Environment

  • Number.cpp and Number.h at develop 1d7669f528ce83113c1ef2d1c8ea4675ce19157d. The loop is identical in 3.4.0.
  • Ubuntu 24.04.4 LTS, g++ 13.3.0, -std=c++23 -O2.

Supporting Files

None.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions