Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

sqrt.gno

3.48 Kb · 116 lines
  1package physics
  2
  3import "math"
  4
  5// fsqrt is math.Sqrt, bit for bit, at a fraction of its gas.
  6//
  7// gno's math.Sqrt computes the root one bit at a time in interpreted code
  8// (~170k gas); the engine takes several roots every millisecond step. Here a
  9// float Newton iteration gets within a unit in the last place, and an exact
 10// integer test settles the last bit: for x = M * 2^e (e even) the correctly
 11// rounded root is R * 2^q, q = e/2 - 26, where R is the integer nearest
 12// sqrt(N), N = M * 2^52, and R is that integer exactly when
 13// R*R - R < N <= R*R + R (no square root of an integer lies halfway between
 14// two integers). N - R*R is small, so it is computed exactly mod 2^64. A
 15// Newton result one ulp off is moved by that ulp; anything else (rare) goes
 16// to sqrtSlow. Zero, subnormals, infinities and NaN go to math.Sqrt.
 17func fsqrt(x float64) float64 {
 18	b := math.Float64bits(x)
 19	if b-1<<52 >= 0x7fe0000000000000 { // negative, zero, subnormal, infinite or NaN
 20		return math.Sqrt(x)
 21	}
 22	y := math.Float64frombits((b >> 1) + 0x1ff8000000000000)
 23	y = 0.5 * (y + x/y)
 24	y = 0.5 * (y + x/y)
 25	y = 0.5 * (y + x/y)
 26	y = 0.5 * (y + x/y)
 27	yb := math.Float64bits(y)
 28	// x = m * 2^(ex), y = R * 2^q: y is the root when R*R - R < N <= R*R + R
 29	// for N = m * 2^(ex-2q), which is m << 52 or m << 53 when y is in the
 30	// root's binade.
 31	s := b>>52 - yb>>52<<1 + 1075
 32	if s-52 > 1 {
 33		return sqrtSlow(x)
 34	}
 35	R := yb&(1<<52-1) | 1<<52
 36	N := (b&(1<<52-1) | 1<<52) << s
 37	d := int64(N - R*R)
 38	if d > int64(R) {
 39		// One ulp low: R+1 is the root when N - (R+1)^2 = d - 2R - 1 is in range.
 40		if d2 := d - int64(R<<1) - 1; d2 <= int64(R+1) && d2 > -int64(R+1) && R+1 < 1<<53 {
 41			return math.Float64frombits(yb + 1)
 42		}
 43		return sqrtSlow(x)
 44	}
 45	if d <= -int64(R) {
 46		if d2 := d + int64(R<<1) - 1; d2 <= int64(R-1) && d2 > -int64(R-1) && R > 1<<52 {
 47			return math.Float64frombits(yb - 1)
 48		}
 49		return sqrtSlow(x)
 50	}
 51	return y
 52}
 53
 54// sqrtSlow settles the roots the fast path cannot: the general case.
 55func sqrtSlow(x float64) float64 {
 56	b := math.Float64bits(x)
 57	exp := int(b>>52) & 0x7ff
 58	if b>>63 != 0 || exp == 0 || exp == 0x7ff {
 59		return math.Sqrt(x)
 60	}
 61	// Halve the exponent, then four Newton steps: within an ulp.
 62	y := math.Float64frombits((b >> 1) + 0x1ff8000000000000)
 63	y = 0.5 * (y + x/y)
 64	y = 0.5 * (y + x/y)
 65	y = 0.5 * (y + x/y)
 66	y = 0.5 * (y + x/y)
 67
 68	m := b&(1<<52-1) | 1<<52
 69	e := exp - 1075 // x = m * 2^e
 70	if e&1 != 0 {
 71		m <<= 1
 72		e--
 73	}
 74	N := m << 52
 75	yb := math.Float64bits(y)
 76	if int(yb>>52) != e/2+1049 { // y is not in the root's binade: settle it the long way
 77		return rootOf(N, e/2-26, y)
 78	}
 79	R := yb&(1<<52-1) | 1<<52 // y = R * 2^q
 80	d := int64(N - R*R)
 81	if d > int64(R) {
 82		if d2 := int64(N - (R+1)*(R+1)); d2 > int64(R+1) || d2 <= -int64(R+1) {
 83			return rootOf(N, e/2-26, y)
 84		}
 85		return math.Float64frombits(yb + 1)
 86	}
 87	if d <= -int64(R) {
 88		if R == 1<<52 {
 89			return rootOf(N, e/2-26, y)
 90		}
 91		if d2 := int64(N - (R-1)*(R-1)); d2 > int64(R-1) || d2 <= -int64(R-1) {
 92			return rootOf(N, e/2-26, y)
 93		}
 94		return math.Float64frombits(yb - 1)
 95	}
 96	return y
 97}
 98
 99// rootOf is the general case: R from the estimate y, an integer Newton step,
100// then whole units until R is the nearest integer to sqrt(N).
101func rootOf(N uint64, q int, y float64) float64 {
102	R := uint64(y * math.Float64frombits(uint64(1023-q)<<52))
103	d := int64(N - R*R)
104	R = uint64(int64(R) + d/int64(2*R))
105	for {
106		d = int64(N - R*R)
107		if d > int64(R) {
108			R++
109		} else if d <= -int64(R) {
110			R--
111		} else {
112			break
113		}
114	}
115	return float64(R) * math.Float64frombits(uint64(q+1023)<<52)
116}