/
eight_rooty_pieces.cpp
241 lines (193 loc) · 5.58 KB
/
eight_rooty_pieces.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#include <limits.h>
#include <math.h>
#include <cmath>
#include <iostream>
/**
* explanation: closed form for the win!
*
* ok ... log x^y = y log x
*/
double my_sqrt(double val) { return exp(0.5 * log(val)); }
/**
* graphical explanation: iterative search for square root by successive
* reduction of difference
* between the 2 sides of a shape with the area of val.
* pick one side, - derive the other - split the difference for the next guess
* Note the expression reduces to 2*x = 2*x when x*x = val
*/
double my_sqrt_bablyonian(double val) {
double x = val / 2;
while (fabs((x * x) - val) > (val / 1E9)) {
x = 0.5 * (x + (val / x));
}
return x;
}
/**
* explanation: NR search for x^2 - value is 0
* - note this relies upon knowing dy/dx is 2*x
* to avoid having to implement the full numerical gradient
* graphical explanation: search for the zero by building a triangle from the
* current guess value
* and the gradient at that point, for which the prior factoid is handy, else a
* numerically estimated gradient
* will do, with the usual caveats about loss of precision - suggest this is a
* good discussion point
*/
double my_sqrt_newtonraphson(double val) {
long double x = val / 2;
while (fabs((x * x) - val) > (val / 1E9)) {
// x * x - val is the function for which we seek the root
x = x - ((x * x - val) / (2 * x));
}
return x;
}
/**
* explanation: range reduction approach (does not rely upon good initial guess)
* note that in contrast to techniques making use of the gradient of the
* function,
* the initial guesses need to cater for the value 1.
* Note: rarely found in the wild as other better sqrt approaches are so well
* known.
*/
double my_sqrt_range(double val) {
double upper = val;
double lower = 1;
if (val < 1) {
upper = 1;
lower = 0;
}
double x = (lower + upper) / 2;
int iterations = 0;
while (iterations < 1000) {
if (((x * x) > val))
upper = x;
else
lower = x;
x = (lower + upper) / 2;
iterations++;
}
return x;
}
/**
* explanation: very naive guess step and scan approach, reversing and
* decreasing step on each transition
* vulnerable to overshoot if the transition is missed
* discussion point: as an improvement the step could always be adjusted to head
* "the right way",
* but the magnitude would only be adjusted upon a crossing
* other discussion point - it turns out that the convergence is
* sensitive to the accuracy of the function near the roots - if loss of
* precision leads to jagged
* behaviour convergence may be be poor
*
*/
double my_sqrt_naive(double val) {
double x = val / 2;
double step = val / 2;
double lastdiff = 0;
double diff = (x * x) - val;
while (fabs(diff) > (val / 1E9)) {
if (diff > 0)
x -= step;
else
x += step;
if ((diff > 0) != (lastdiff > 0)) {
step = step * 0.5;
}
lastdiff = diff;
diff = (x * x) - val;
}
return x;
}
/**
*
* a one-sided binary search version -
* double successively to obtain the upper range, use zero for the lower
* note starting with 1, upper will be found in zero passes for y < 1
*
*/
double my_sqrt_binary(double val) {
double lower = 0;
double upper = val;
while ((upper * upper) < val)
upper *= 2;
double x = (lower + upper) / 2;
int iterations = 0;
while (iterations < 30) {
if (((x * x) > val))
upper = x;
else
lower = x;
x = (lower + upper) / 2;
iterations++;
}
return x;
}
/**
* the point that any monotonically rising function attains a given value can be
* put in here
*/
int is_one(double guess, double val) { return ((guess * guess) > val) ? 1 : 0; }
/**
*
* a one-sided binary search version that will seek the edge-
* double successively to obtain the upper range, use zero for the lower
*
*/
double my_sqrt_binary_for_joao(double val) {
double lower = 0;
double upper = 0.01; // selecting a good initial guess for the whole domain
// is actually worthy of another pub discussion...
while (!is_one(upper, val))
upper *= 2;
double x = (lower + upper) / 2;
while (fabs(1 - (lower / upper)) > 1e-8) {
if (is_one(x, val))
upper = x;
else
lower = x;
x = (lower + upper) / 2;
}
return x;
}
/**
* explanation: just for fun, old example of a very fast approximate inverse
* square root,
* still works on Intel
*/
double my_sqrt_homage_to_carmack(float x) {
// actually Chris Lomont version via SO, allegedly
// note: are we running on Little-Endian, hmm? :P
float xhalf = 0.5f * x;
int i = *(int *)&x; // get bits for floating value
i = 0x5f375a86 - (i >> 1); // gives initial guess y0
x = *(float *)&i; // convert bits back to float
// initial guess: to within 10% already!
x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy
// gah! no iterations!!!!
return 1 / x;
}
/**
* explanation: update of the original to 64-bit types
*/
double my_sqrt_homage_to_carmack64(double x) {
double xhalf = x * 0.5;
long long i = *(long long *)&x; // get bits for floating value
i = 0x5fe6eb50c7b537a9 - (i >> 1); // gives initial guess y0
x = *(double *)&i; // convert bits back into double
x = x * (1.5f - xhalf * x * x); // one Newton Raphson step
return 1 / x;
}
/**
* explanation: inverse square root algorithm
*/
double my_inverse_sqrt(double x) {
long long i, r;
double xhalf = x * 0.5, y = x;
i = *(long long *)&x;
i = 0x5fe6eb50c7b537a9 - (i >> 1);
y = *(double *)&i;
for (r = 0; r < 10; r++)
y = y * (1.5 - (xhalf * y * y));
return x * y;
}