Three answers to the same problem
Iterate. Curve's get_D and get_y each run a Newton–Raphson loop written as for _i in range(255). Each pass computes a better estimate and compares it to the previous one; the loop breaks the moment the two differ by 1 or less, under a source comment that reads “Equality with the precision of 1”. That unit is the smallest representable amount, so the loop is asking for agreement to the last decimal place the token has. At ordinary balances it gets there in a handful of passes. The 255 is a ceiling, not an expectation.
Approximate. Balancer does not iterate. LogExpMath.pow rewrites xy as exp(y · ln x) and builds both halves out of fixed-point series, carrying intermediate values at 20 decimals and switching to a separate 36-decimal path when the base sits within 0.1 of one, where precision matters most. The domain is bounded by the word size rather than by economics: the source derives the largest usable exponent as ln((2255 − 1) / 1020) = 130.70, then pins the constant at 130 “to have some safety margin”, with the negative bound at −41. A trade whose exponent lands outside that window reverts.
Precompute. Uniswap v3 avoids the question by shrinking the set of legal prices. Prices live on ticks of 1.0001t, and TickMath.getSqrtRatioAtTick converts a tick to a price with no loop at all: it decomposes the tick index into bits and multiplies a hard-coded constant for each set bit, nineteen constants covering the whole range from tick −887272 to 887272. Every one was computed off-chain and burned into the bytecode. The result is exact and costs a fixed amount of gas, and the price grid is discrete by construction – the v3 paper treats that granularity as the design, not a compromise.
The three sit on one trade-off. You can search for the answer and pay in iterations, approximate it and pay in error, or restrict the question and pay in expressiveness. None of them gets exactness for free.