32
32
#include "bcmath.h"
33
33
#include <stdbool.h>
34
34
#include <stddef.h>
35
+ #include "private.h"
35
36
36
37
/* Take the square root NUM and return it in NUM with SCALE digits
37
38
after the decimal place. */
38
39
40
+ static inline BC_VECTOR bc_sqrt_get_pow_10 (size_t exponent )
41
+ {
42
+ BC_VECTOR value = 1 ;
43
+ while (exponent >= 10 ) {
44
+ value *= BC_POW_10_LUT [10 ];
45
+ exponent -= 10 ;
46
+ }
47
+ value *= BC_POW_10_LUT [exponent ];
48
+ return value ;
49
+ }
50
+
39
51
bool bc_sqrt (bc_num * num , size_t scale )
40
52
{
41
53
/* Initial checks. */
@@ -59,55 +71,108 @@ bool bc_sqrt(bc_num *num, size_t scale)
59
71
}
60
72
61
73
/* Initialize the variables. */
62
- size_t cscale ;
63
- bc_num guess ;
64
74
size_t rscale = MAX (scale , (* num )-> n_scale );
65
75
66
- /* Calculate the initial guess. */
67
- if (num_cmp_one == BCMATH_RIGHT_GREATER ) {
68
- /* The number is between 0 and 1. Guess should start at 1. */
69
- guess = bc_copy_num (BCG (_one_ ));
70
- cscale = (* num )-> n_scale ;
71
- } else {
72
- /* The number is greater than 1. Guess should start at 10^(exp/2). */
73
- /* If just divide size_t by 2 it will not overflow. */
74
- size_t exponent_for_initial_guess = (size_t ) (* num )-> n_len >> 1 ;
75
-
76
- /* 10^n is a 1 followed by n zeros. */
77
- guess = bc_new_num (exponent_for_initial_guess + 1 , 0 );
78
- guess -> n_value [0 ] = 1 ;
79
- cscale = 3 ;
80
- }
76
+ /* Find the square root using Newton's algorithm. */
77
+ if ((* num )-> n_len + (rscale + 1 ) * 2 < sizeof (LONG_MIN_DIGITS ) - 1 ) {
78
+ /* fast path */
81
79
82
- bc_num guess1 = NULL ;
83
- bc_num point5 = bc_new_num (1 , 1 );
84
- point5 -> n_value [1 ] = 5 ;
85
- bc_num diff = NULL ;
80
+ /* Calculate the initial guess. */
81
+ BC_VECTOR guess_vector = 1 ;
82
+ if (num_cmp_one != BCMATH_RIGHT_GREATER ) {
83
+ guess_vector *= bc_sqrt_get_pow_10 (((* num )-> n_len + rscale + 1 ) >> 1 );
84
+ }
86
85
87
- /* Find the square root using Newton's algorithm. */
88
- bool done = false;
89
- while (!done ) {
90
- bc_free_num (& guess1 );
91
- guess1 = bc_copy_num (guess );
92
- bc_divide (* num , guess , & guess , cscale );
93
- bc_add_ex (guess , guess1 , & guess , 0 );
94
- bc_multiply_ex (guess , point5 , & guess , cscale );
95
- bc_sub_ex (guess , guess1 , & diff , cscale + 1 );
96
- if (bc_is_near_zero (diff , cscale )) {
97
- if (cscale < rscale + 1 ) {
98
- cscale = MIN (cscale * 3 , rscale + 1 );
99
- } else {
86
+ BC_VECTOR n_vector = 0 ;
87
+ size_t i = 0 ;
88
+ for (; i < (* num )-> n_len + (* num )-> n_scale ; i ++ ) {
89
+ n_vector = n_vector * BASE + (* num )-> n_value [i ];
90
+ }
91
+ n_vector *= bc_sqrt_get_pow_10 ((rscale + 1 ) * 2 - (* num )-> n_scale );
92
+
93
+ BC_VECTOR guess1_vector ;
94
+ bool done = false;
95
+ while (!done ) {
96
+ guess1_vector = guess_vector ;
97
+ guess_vector = n_vector / guess_vector ;
98
+ guess_vector += guess1_vector ;
99
+ guess_vector /= 2 ;
100
+ size_t diff = guess1_vector > guess_vector ? guess1_vector - guess_vector : guess_vector - guess1_vector ;
101
+ if (diff <= 1 ) {
100
102
done = true;
101
103
}
102
104
}
105
+
106
+ size_t full_len = 0 ;
107
+ BC_VECTOR tmp_guess_vector = guess_vector ;
108
+ do {
109
+ tmp_guess_vector /= BASE ;
110
+ full_len ++ ;
111
+ } while (tmp_guess_vector > 0 );
112
+
113
+ size_t ret_ren = full_len > rscale + 1 ? full_len - (rscale + 1 ) : 1 ; /* for int zero */
114
+ bc_num ret = bc_new_num_nonzeroed (ret_ren , rscale );
115
+ char * rptr = ret -> n_value ;
116
+ char * rend = rptr + ret_ren + rscale - 1 ;
117
+
118
+ guess_vector /= BASE ;
119
+ while (rend >= rptr ) {
120
+ * rend -- = guess_vector % BASE ;
121
+ guess_vector /= BASE ;
122
+ }
123
+ bc_free_num (num );
124
+ * num = ret ;
125
+ } else {
126
+ /* standard path */
127
+
128
+ bc_num guess ;
129
+ size_t cscale ;
130
+ /* Calculate the initial guess. */
131
+ if (num_cmp_one == BCMATH_RIGHT_GREATER ) {
132
+ /* The number is between 0 and 1. Guess should start at 1. */
133
+ guess = bc_copy_num (BCG (_one_ ));
134
+ cscale = (* num )-> n_scale ;
135
+ } else {
136
+ /* The number is greater than 1. Guess should start at 10^(exp/2). */
137
+ /* If just divide size_t by 2 it will not overflow. */
138
+ size_t exponent_for_initial_guess = (size_t ) (* num )-> n_len >> 1 ;
139
+
140
+ /* 10^n is a 1 followed by n zeros. */
141
+ guess = bc_new_num (exponent_for_initial_guess + 1 , 0 );
142
+ guess -> n_value [0 ] = 1 ;
143
+ cscale = 3 ;
144
+ }
145
+
146
+ bc_num guess1 = NULL ;
147
+ bc_num point5 = bc_new_num (1 , 1 );
148
+ point5 -> n_value [1 ] = 5 ;
149
+ bc_num diff = NULL ;
150
+
151
+ bool done = false;
152
+ while (!done ) {
153
+ bc_free_num (& guess1 );
154
+ guess1 = bc_copy_num (guess );
155
+ bc_divide (* num , guess , & guess , cscale );
156
+ bc_add_ex (guess , guess1 , & guess , 0 );
157
+ bc_multiply_ex (guess , point5 , & guess , cscale );
158
+ bc_sub_ex (guess , guess1 , & diff , cscale + 1 );
159
+ if (bc_is_near_zero (diff , cscale )) {
160
+ if (cscale < rscale + 1 ) {
161
+ cscale = MIN (cscale * 3 , rscale + 1 );
162
+ } else {
163
+ done = true;
164
+ }
165
+ }
166
+ }
167
+
168
+ /* Assign the number and clean up. */
169
+ bc_free_num (num );
170
+ bc_divide (guess , BCG (_one_ ), num , rscale );
171
+ bc_free_num (& guess );
172
+ bc_free_num (& guess1 );
173
+ bc_free_num (& point5 );
174
+ bc_free_num (& diff );
103
175
}
104
176
105
- /* Assign the number and clean up. */
106
- bc_free_num (num );
107
- bc_divide (guess , BCG (_one_ ), num , rscale );
108
- bc_free_num (& guess );
109
- bc_free_num (& guess1 );
110
- bc_free_num (& point5 );
111
- bc_free_num (& diff );
112
177
return true;
113
178
}
0 commit comments