CubeRoot.cpp revision 9e49fb63d355446b91d20ff78ad78b297e89a50d
1/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7// http://metamerist.com/cbrt/CubeRoot.cpp
8//
9
10#include <math.h>
11#include "CubicUtilities.h"
12
13#define TEST_ALTERNATIVES 0
14#if TEST_ALTERNATIVES
15typedef float  (*cuberootfnf) (float);
16typedef double (*cuberootfnd) (double);
17
18// estimate bits of precision (32-bit float case)
19inline int bits_of_precision(float a, float b)
20{
21    const double kd = 1.0 / log(2.0);
22
23    if (a==b)
24        return 23;
25
26    const double kdmin = pow(2.0, -23.0);
27
28    double d = fabs(a-b);
29    if (d < kdmin)
30        return 23;
31
32    return int(-log(d)*kd);
33}
34
35// estiamte bits of precision (64-bit double case)
36inline int bits_of_precision(double a, double b)
37{
38    const double kd = 1.0 / log(2.0);
39
40    if (a==b)
41        return 52;
42
43    const double kdmin = pow(2.0, -52.0);
44
45    double d = fabs(a-b);
46    if (d < kdmin)
47        return 52;
48
49    return int(-log(d)*kd);
50}
51
52// cube root via x^(1/3)
53static float pow_cbrtf(float x)
54{
55    return (float) pow(x, 1.0f/3.0f);
56}
57
58// cube root via x^(1/3)
59static double pow_cbrtd(double x)
60{
61    return pow(x, 1.0/3.0);
62}
63
64// cube root approximation using bit hack for 32-bit float
65static  float cbrt_5f(float f)
66{
67    unsigned int* p = (unsigned int *) &f;
68    *p = *p/3 + 709921077;
69    return f;
70}
71#endif
72
73// cube root approximation using bit hack for 64-bit float
74// adapted from Kahan's cbrt
75static  double cbrt_5d(double d)
76{
77    const unsigned int B1 = 715094163;
78    double t = 0.0;
79    unsigned int* pt = (unsigned int*) &t;
80    unsigned int* px = (unsigned int*) &d;
81    pt[1]=px[1]/3+B1;
82    return t;
83}
84
85#if TEST_ALTERNATIVES
86// cube root approximation using bit hack for 64-bit float
87// adapted from Kahan's cbrt
88#if 0
89static  double quint_5d(double d)
90{
91    return sqrt(sqrt(d));
92
93    const unsigned int B1 = 71509416*5/3;
94    double t = 0.0;
95    unsigned int* pt = (unsigned int*) &t;
96    unsigned int* px = (unsigned int*) &d;
97    pt[1]=px[1]/5+B1;
98    return t;
99}
100#endif
101
102// iterative cube root approximation using Halley's method (float)
103static  float cbrta_halleyf(const float a, const float R)
104{
105    const float a3 = a*a*a;
106    const float b= a * (a3 + R + R) / (a3 + a3 + R);
107    return b;
108}
109#endif
110
111// iterative cube root approximation using Halley's method (double)
112static  double cbrta_halleyd(const double a, const double R)
113{
114    const double a3 = a*a*a;
115    const double b= a * (a3 + R + R) / (a3 + a3 + R);
116    return b;
117}
118
119#if TEST_ALTERNATIVES
120// iterative cube root approximation using Newton's method (float)
121static  float cbrta_newtonf(const float a, const float x)
122{
123//    return (1.0 / 3.0) * ((a + a) + x / (a * a));
124    return a - (1.0f / 3.0f) * (a - x / (a*a));
125}
126
127// iterative cube root approximation using Newton's method (double)
128static  double cbrta_newtond(const double a, const double x)
129{
130    return (1.0/3.0) * (x / (a*a) + 2*a);
131}
132
133// cube root approximation using 1 iteration of Halley's method (double)
134static double halley_cbrt1d(double d)
135{
136    double a = cbrt_5d(d);
137    return cbrta_halleyd(a, d);
138}
139
140// cube root approximation using 1 iteration of Halley's method (float)
141static float halley_cbrt1f(float d)
142{
143    float a = cbrt_5f(d);
144    return cbrta_halleyf(a, d);
145}
146
147// cube root approximation using 2 iterations of Halley's method (double)
148static double halley_cbrt2d(double d)
149{
150    double a = cbrt_5d(d);
151    a = cbrta_halleyd(a, d);
152    return cbrta_halleyd(a, d);
153}
154#endif
155
156// cube root approximation using 3 iterations of Halley's method (double)
157static double halley_cbrt3d(double d)
158{
159    double a = cbrt_5d(d);
160    a = cbrta_halleyd(a, d);
161    a = cbrta_halleyd(a, d);
162    return cbrta_halleyd(a, d);
163}
164
165#if TEST_ALTERNATIVES
166// cube root approximation using 2 iterations of Halley's method (float)
167static float halley_cbrt2f(float d)
168{
169    float a = cbrt_5f(d);
170    a = cbrta_halleyf(a, d);
171    return cbrta_halleyf(a, d);
172}
173
174// cube root approximation using 1 iteration of Newton's method (double)
175static double newton_cbrt1d(double d)
176{
177    double a = cbrt_5d(d);
178    return cbrta_newtond(a, d);
179}
180
181// cube root approximation using 2 iterations of Newton's method (double)
182static double newton_cbrt2d(double d)
183{
184    double a = cbrt_5d(d);
185    a = cbrta_newtond(a, d);
186    return cbrta_newtond(a, d);
187}
188
189// cube root approximation using 3 iterations of Newton's method (double)
190static double newton_cbrt3d(double d)
191{
192    double a = cbrt_5d(d);
193    a = cbrta_newtond(a, d);
194    a = cbrta_newtond(a, d);
195    return cbrta_newtond(a, d);
196}
197
198// cube root approximation using 4 iterations of Newton's method (double)
199static double newton_cbrt4d(double d)
200{
201    double a = cbrt_5d(d);
202    a = cbrta_newtond(a, d);
203    a = cbrta_newtond(a, d);
204    a = cbrta_newtond(a, d);
205    return cbrta_newtond(a, d);
206}
207
208// cube root approximation using 2 iterations of Newton's method (float)
209static float newton_cbrt1f(float d)
210{
211    float a = cbrt_5f(d);
212    return cbrta_newtonf(a, d);
213}
214
215// cube root approximation using 2 iterations of Newton's method (float)
216static float newton_cbrt2f(float d)
217{
218    float a = cbrt_5f(d);
219    a = cbrta_newtonf(a, d);
220    return cbrta_newtonf(a, d);
221}
222
223// cube root approximation using 3 iterations of Newton's method (float)
224static float newton_cbrt3f(float d)
225{
226    float a = cbrt_5f(d);
227    a = cbrta_newtonf(a, d);
228    a = cbrta_newtonf(a, d);
229    return cbrta_newtonf(a, d);
230}
231
232// cube root approximation using 4 iterations of Newton's method (float)
233static float newton_cbrt4f(float d)
234{
235    float a = cbrt_5f(d);
236    a = cbrta_newtonf(a, d);
237    a = cbrta_newtonf(a, d);
238    a = cbrta_newtonf(a, d);
239    return cbrta_newtonf(a, d);
240}
241
242static double TestCubeRootf(const char* szName, cuberootfnf cbrt, double rA, double rB, int rN)
243{
244    const int N = rN;
245
246    float dd = float((rB-rA) / N);
247
248    // calculate 1M numbers
249    int i=0;
250    float d = (float) rA;
251
252    double s = 0.0;
253
254    for(d=(float) rA, i=0; i<N; i++, d += dd)
255    {
256        s += cbrt(d);
257    }
258
259    double bits = 0.0;
260    double worstx=0.0;
261    double worsty=0.0;
262    int minbits=64;
263
264    for(d=(float) rA, i=0; i<N; i++, d += dd)
265    {
266        float a = cbrt((float) d);
267        float b = (float) pow((double) d, 1.0/3.0);
268
269        int bc = bits_of_precision(a, b);
270        bits += bc;
271
272        if (b > 1.0e-6)
273        {
274            if (bc < minbits)
275            {
276                minbits = bc;
277                worstx = d;
278                worsty = a;
279            }
280        }
281    }
282
283    bits /= N;
284
285    printf(" %3d mbp  %6.3f abp\n", minbits, bits);
286
287    return s;
288}
289
290
291static double TestCubeRootd(const char* szName, cuberootfnd cbrt, double rA, double rB, int rN)
292{
293    const int N = rN;
294
295    double dd = (rB-rA) / N;
296
297    int i=0;
298
299    double s = 0.0;
300    double d = 0.0;
301
302    for(d=rA, i=0; i<N; i++, d += dd)
303    {
304        s += cbrt(d);
305    }
306
307
308    double bits = 0.0;
309    double worstx = 0.0;
310    double worsty = 0.0;
311    int minbits = 64;
312    for(d=rA, i=0; i<N; i++, d += dd)
313    {
314        double a = cbrt(d);
315        double b = pow(d, 1.0/3.0);
316
317        int bc = bits_of_precision(a, b); // min(53, count_matching_bitsd(a, b) - 12);
318        bits += bc;
319
320        if (b > 1.0e-6)
321        {
322            if (bc < minbits)
323            {
324                bits_of_precision(a, b);
325                minbits = bc;
326                worstx = d;
327                worsty = a;
328            }
329        }
330    }
331
332    bits /= N;
333
334    printf(" %3d mbp  %6.3f abp\n", minbits, bits);
335
336    return s;
337}
338
339static int _tmain()
340{
341    // a million uniform steps through the range from 0.0 to 1.0
342    // (doing uniform steps in the log scale would be better)
343    double a = 0.0;
344    double b = 1.0;
345    int n = 1000000;
346
347    printf("32-bit float tests\n");
348    printf("----------------------------------------\n");
349    TestCubeRootf("cbrt_5f", cbrt_5f, a, b, n);
350    TestCubeRootf("pow", pow_cbrtf, a, b, n);
351    TestCubeRootf("halley x 1", halley_cbrt1f, a, b, n);
352    TestCubeRootf("halley x 2", halley_cbrt2f, a, b, n);
353    TestCubeRootf("newton x 1", newton_cbrt1f, a, b, n);
354    TestCubeRootf("newton x 2", newton_cbrt2f, a, b, n);
355    TestCubeRootf("newton x 3", newton_cbrt3f, a, b, n);
356    TestCubeRootf("newton x 4", newton_cbrt4f, a, b, n);
357    printf("\n\n");
358
359    printf("64-bit double tests\n");
360    printf("----------------------------------------\n");
361    TestCubeRootd("cbrt_5d", cbrt_5d, a, b, n);
362    TestCubeRootd("pow", pow_cbrtd, a, b, n);
363    TestCubeRootd("halley x 1", halley_cbrt1d, a, b, n);
364    TestCubeRootd("halley x 2", halley_cbrt2d, a, b, n);
365    TestCubeRootd("halley x 3", halley_cbrt3d, a, b, n);
366    TestCubeRootd("newton x 1", newton_cbrt1d, a, b, n);
367    TestCubeRootd("newton x 2", newton_cbrt2d, a, b, n);
368    TestCubeRootd("newton x 3", newton_cbrt3d, a, b, n);
369    TestCubeRootd("newton x 4", newton_cbrt4d, a, b, n);
370    printf("\n\n");
371
372    return 0;
373}
374#endif
375
376double cube_root(double x) {
377    return halley_cbrt3d(x);
378}
379
380#if TEST_ALTERNATIVES
381// http://bytes.com/topic/c/answers/754588-tips-find-cube-root-program-using-c
382/* cube root */
383int icbrt(int n) {
384    int t=0, x=(n+2)/3; /* works for n=0 and n>=1 */
385    for(; t!=x;) {
386        int x3=x*x*x;
387        t=x;
388        x*=(2*n + x3);
389        x/=(2*x3 + n);
390    }
391    return x ; /* always(?) equal to floor(n^(1/3)) */
392}
393#endif
394