calculate the nth root of an arbitrary uint using solidity

10^(1/8) is 1.33352143. I'm looking for a solidity function that would return 133352143 here given inputs 10 and 8.

I've looked around for something that can do this with no luck.

The following is put together quickly, and is minimally tested. It uses the Newton--Raphson method to solve `x^n - a = 0` and seems fairly efficient.

``````contract Root {

// calculates a^(1/n) to dp decimal places
// maxIts bounds the number of iterations performed
function nthRoot(uint _a, uint _n, uint _dp, uint _maxIts) pure public returns(uint) {
assert (_n > 1);

// The scale factor is a crude way to turn everything into integer calcs.
// Actually do (a * (10 ^ ((dp + 1) * n))) ^ (1/n)
// We calculate to one extra dp and round at the end
uint one = 10 ** (1 + _dp);
uint a0 = one ** _n * _a;

// Initial guess: 1.0
uint xNew = one;

uint iter = 0;
while (xNew != x && iter < _maxIts) {
uint x = xNew;
uint t0 = x ** (_n - 1);
if (x * t0 > a0) {
xNew = x - (x - a0 / t0) / _n;
} else {
xNew = x + (a0 / t0 - x) / _n;
}
++iter;
}

// Round to nearest in the last dp.
return (xNew + 5) / 10;
}
}
``````

On your input, `nthRoot(10, 8, 8, 10)` => `133352143` using 3674 gas (over and above the gas for calling a contract). The last parameter is just a safety-net: you can set it much higher to ensure you get as much accuracy as required.

Beware of overflows when `n` * `dp` is "large" - I'm not doing any checking for this. In fact, your input is only just manageable. One more decimal place or one more power of n and it overflows. It's likely possible to fix this, and I'll update the answer if come up with something. But for low powers and relatively low numbers of decimals it is very usable.

• this doesn't accommodate numbers with 18 decimals Commented Oct 22, 2019 at 17:14