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.


Reply via email to