To be honest, 1. There is no need to compute `pow(E, result)` twice. 2. A fast numerical way should use polynomial approximation. see [musl implementation](https://git.musl-libc.org/cgit/musl/tree/src/math/log.c) 3. `pow` should not be used, not to say inside while loop. Also, implementation of `pow` often calls `log` internally. 4. Newton method is a fast method (the approximation speed is linear to accuracy per iteration), but it doesn't mean direct implementation is fast. 5. A simple binary search (the approximation speed is constant per iteration) is possible by making use of some basic properties `log2(x) = 1+log2(x/2) = log2(x*x)/2`, no external library needed, no table lookup needed, no special constant needed.
import std/monotimes from std/math import pow, almostEqual, E, log2 from std/fenv import epsilon proc simpleNewton(n: float64): float64 = var next: float64 = 1.0 while not almostEqual(result, next, 1): result = next next = result - 1 + n/pow(2, result) proc binarySearch(n: float64): float64 = var x = n var c = 1.0 while c > epsilon(float64): if x >= 2: result += c x /= 2 elif x <= 1/2: result -= c x *= 2 else: x = x*x c /= 2 var t0 = high(int) template timeit(name, code) = block: let s = getMonoTime() var ans = 0.0 for i in 1..1000: ans += code(i.float) let e = getMonoTime() let t = e.ticks - s.ticks t0 = min(t0, t) # echo t echo name, ": time=", t, "\t(x", t/t0, ")\tans=", ans timeit "math.log2 ", log2 timeit "binarySearch", binarySearch timeit "simpleNewton", simpleNewton # nim r -d:release play_log.nim # math.log : time=28050 (x1.0) ans=8529.398004204777 # binarySearch: time=762358 (x27.17853832442068) ans=8529.398004204777 # simpleNewton: time=16559248 (x590.34752228164) ans=8529.398004204781 Run 6. The way calculating `result` in simpleNewton **may** accumulates and propagates numerical error sightly higher than the others as shown above.