The Sqrt Newton method computes g(t) = f(t)/f'(t) and then iterates
t2 = t1 - g(t1)
We can save one operation by including the final subtraction in g(t)
and evaluating the resulting expression symbolically.
For example, for the direct method,
g(t) = ½(t² - x)/t
and we use 2 multiplications, 1 division and 1 subtraction in g(),
plus 1 final subtraction; but if we compute
t - g(t) = t - ½(t² - x)/t = ½(t² + x)/t
we only use 2 multiplications, 1 division and 1 addition.
A similar simplification can be done for the inverse method.
name old time/op new time/op delta
FloatSqrt/64-4 889ns ± 4% 790ns ± 1% -11.19% (p=0.000 n=8+7)
FloatSqrt/128-4 1.82µs ± 0% 1.64µs ± 1% -10.07% (p=0.001 n=6+8)
FloatSqrt/256-4 3.56µs ± 4% 3.10µs ± 3% -12.96% (p=0.000 n=7+8)
FloatSqrt/1000-4 9.06µs ± 3% 8.86µs ± 1% -2.20% (p=0.001 n=7+7)
FloatSqrt/10000-4 109µs ± 1% 107µs ± 1% -1.56% (p=0.000 n=8+8)
FloatSqrt/100000-4 2.91ms ± 0% 2.89ms ± 2% -0.68% (p=0.026 n=7+7)
FloatSqrt/
1000000-4 237ms ± 1% 239ms ± 1% +0.72% (p=0.021 n=8+8)
name old alloc/op new alloc/op delta
FloatSqrt/64-4 448B ± 0% 416B ± 0% -7.14% (p=0.000 n=8+8)
FloatSqrt/128-4 752B ± 0% 720B ± 0% -4.26% (p=0.000 n=8+8)
FloatSqrt/256-4 2.05kB ± 0% 1.34kB ± 0% -34.38% (p=0.000 n=8+8)
FloatSqrt/1000-4 6.91kB ± 0% 5.09kB ± 0% -26.39% (p=0.000 n=8+8)
FloatSqrt/10000-4 60.5kB ± 0% 45.9kB ± 0% -24.17% (p=0.000 n=8+8)
FloatSqrt/100000-4 617kB ± 0% 533kB ± 0% -13.57% (p=0.000 n=8+8)
FloatSqrt/
1000000-4 10.3MB ± 0% 9.2MB ± 0% -10.85% (p=0.000 n=8+8)
name old allocs/op new allocs/op delta
FloatSqrt/64-4 9.00 ± 0% 9.00 ± 0% ~ (all equal)
FloatSqrt/128-4 13.0 ± 0% 13.0 ± 0% ~ (all equal)
FloatSqrt/256-4 20.0 ± 0% 15.0 ± 0% -25.00% (p=0.000 n=8+8)
FloatSqrt/1000-4 31.0 ± 0% 24.0 ± 0% -22.58% (p=0.000 n=8+8)
FloatSqrt/10000-4 50.0 ± 0% 40.0 ± 0% -20.00% (p=0.000 n=8+8)
FloatSqrt/100000-4 76.0 ± 0% 66.0 ± 0% -13.16% (p=0.000 n=8+8)
FloatSqrt/
1000000-4 146 ± 0% 143 ± 0% -2.05% (p=0.000 n=8+8)
Change-Id: I271c00de1ca9740e585bf2af7bcd87b18c1fa68e
Reviewed-on: https://go-review.googlesource.com/73879
Run-TryBot: Alberto Donizetti <alb.donizetti@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
import "math"
var (
- nhalf = NewFloat(-0.5)
half = NewFloat(0.5)
- one = NewFloat(1.0)
two = NewFloat(2.0)
+ three = NewFloat(3.0)
)
// Sqrt sets z to the rounded square root of x, and returns it.
// f(t) = t² - x
// then
// g(t) = f(t)/f'(t) = ½(t² - x)/t
+ // and the next guess is given by
+ // t2 = t - g(t) = ½(t² + x)/t
u := new(Float)
- g := func(t *Float) *Float {
+ ng := func(t *Float) *Float {
u.prec = t.prec
- u.Mul(t, t) // u = t²
- u.Sub(u, x) // = t² - x
- u.Mul(half, u) // = ½(t² - x)
- u.Quo(u, t) // = ½(t² - x)/t
- return u
+ u.Mul(t, t) // u = t²
+ u.Add(u, x) // = t² + x
+ u.Mul(half, u) // = ½(t² + x)
+ return t.Quo(u, t) // = ½(t² + x)/t
}
xf, _ := x.Float64()
panic("sqrtDirect: only for z.prec <= 128")
case z.prec > 64:
sq.prec *= 2
- sq.Sub(sq, g(sq))
+ sq = ng(sq)
fallthrough
default:
sq.prec *= 2
- sq.Sub(sq, g(sq))
+ sq = ng(sq)
}
z.Set(sq)
// f(t) = 1/t² - x
// then
// g(t) = f(t)/f'(t) = -½t(1 - xt²)
+ // and the next guess is given by
+ // t2 = t - g(t) = ½t(3 - xt²)
u := new(Float)
- g := func(t *Float) *Float {
+ ng := func(t *Float) *Float {
u.prec = t.prec
- u.Mul(t, t) // u = t²
- u.Mul(x, u) // = xt²
- u.Sub(one, u) // = 1 - xt²
- u.Mul(nhalf, u) // = -½(1 - xt²)
- u.Mul(t, u) // = -½t(1 - xt²)
- return u
+ u.Mul(t, t) // u = t²
+ u.Mul(x, u) // = xt²
+ u.Sub(three, u) // = 3 - xt²
+ u.Mul(t, u) // = t(3 - xt²)
+ return t.Mul(half, u) // = ½t(3 - xt²)
}
xf, _ := x.Float64()
sqi := NewFloat(1 / math.Sqrt(xf))
for prec := 2 * z.prec; sqi.prec < prec; {
sqi.prec *= 2
- sqi.Sub(sqi, g(sqi))
+ sqi = ng(sqi)
}
// sqi = 1/√x