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
39
- bool bc_sqrt ( bc_num * num , size_t scale )
40
+ static inline BC_VECTOR bc_sqrt_get_pow_10 ( size_t exponent )
40
41
{
41
- /* Initial checks. */
42
- if ( bc_is_neg ( * num ) ) {
43
- /* Cannot take the square root of a negative number */
44
- return false ;
42
+ BC_VECTOR value = 1 ;
43
+ while ( exponent >= 8 ) {
44
+ value *= BC_POW_10_LUT [ 8 ];
45
+ exponent -= 8 ;
45
46
}
46
- /* Square root of 0 is 0 */
47
- if (bc_is_zero (* num )) {
48
- bc_free_num (num );
49
- * num = bc_copy_num (BCG (_zero_ ));
50
- return true;
47
+ value *= BC_POW_10_LUT [exponent ];
48
+ return value ;
49
+ }
50
+
51
+ static BC_VECTOR bc_fast_sqrt_vector (BC_VECTOR n_vector )
52
+ {
53
+ /* Use sqrt() from <math.h> to determine the initial value. */
54
+ BC_VECTOR guess_vector = (BC_VECTOR ) round (sqrt ((double ) n_vector ));
55
+
56
+ /* Newton's algorithm. Iterative expression is `x_{n+1} = (x_n + a / x_n) / 2` */
57
+ BC_VECTOR guess1_vector ;
58
+ size_t diff ;
59
+ do {
60
+ guess1_vector = guess_vector ;
61
+ guess_vector = (guess1_vector + n_vector / guess1_vector ) / 2 ; /* Iterative expression */
62
+ diff = guess1_vector > guess_vector ? guess1_vector - guess_vector : guess_vector - guess1_vector ;
63
+ } while (diff > 1 );
64
+ return guess_vector ;
65
+ }
66
+
67
+ static inline void bc_fast_sqrt (bc_num * num , size_t scale , size_t num_calc_full_len , size_t num_use_full_len , size_t leading_zeros )
68
+ {
69
+ BC_VECTOR n_vector = 0 ;
70
+ const char * nptr = (* num )-> n_value + leading_zeros ;
71
+ for (size_t i = 0 ; i < num_use_full_len ; i ++ ) {
72
+ n_vector = n_vector * BASE + * nptr ++ ;
73
+ }
74
+ /* When calculating the square root of a number using only integer operations,
75
+ * need to adjust the digit scale accordingly.
76
+ * Considering that the original number is the square of the result,
77
+ * if the desired scale of the result is 5, the input number should be scaled
78
+ * by twice that, i.e., scale 10. */
79
+ n_vector *= bc_sqrt_get_pow_10 (num_calc_full_len - num_use_full_len );
80
+
81
+ /* Get sqrt */
82
+ BC_VECTOR guess_vector = bc_fast_sqrt_vector (n_vector );
83
+
84
+ size_t ret_len ;
85
+ if (leading_zeros > 0 ) {
86
+ ret_len = 1 ;
87
+ } else {
88
+ ret_len = ((* num )-> n_len + 1 ) / 2 ;
51
89
}
52
90
53
- bcmath_compare_result num_cmp_one = bc_compare (* num , BCG (_one_ ), (* num )-> n_scale );
54
- /* Square root of 1 is 1 */
55
- if (num_cmp_one == BCMATH_EQUAL ) {
56
- bc_free_num (num );
57
- * num = bc_copy_num (BCG (_one_ ));
58
- return true;
91
+ bc_num ret = bc_new_num_nonzeroed (ret_len , scale );
92
+ char * rptr = ret -> n_value ;
93
+ char * rend = rptr + ret_len + scale - 1 ;
94
+
95
+ guess_vector /= BASE ; /* Since the scale of guess_vector is scale + 1, reduce the scale by 1. */
96
+ while (rend >= rptr ) {
97
+ * rend -- = guess_vector % BASE ;
98
+ guess_vector /= BASE ;
59
99
}
100
+ bc_free_num (num );
101
+ * num = ret ;
102
+ }
60
103
61
- /* Initialize the variables. */
62
- size_t cscale ;
104
+ static inline void bc_standard_sqrt ( bc_num * num , size_t scale , bcmath_compare_result num_cmp_one )
105
+ {
63
106
bc_num guess ;
64
- size_t rscale = MAX (scale , (* num )-> n_scale );
65
-
107
+ size_t cscale ;
66
108
/* Calculate the initial guess. */
67
109
if (num_cmp_one == BCMATH_RIGHT_GREATER ) {
68
110
/* The number is between 0 and 1. Guess should start at 1. */
@@ -86,7 +128,6 @@ bool bc_sqrt(bc_num *num, size_t scale)
86
128
point5 -> n_value [1 ] = 5 ;
87
129
bc_num diff = NULL ;
88
130
89
- /* Find the square root using Newton's algorithm. */
90
131
bool done = false;
91
132
while (!done ) {
92
133
bc_free_num (& guess1 );
@@ -96,8 +137,8 @@ bool bc_sqrt(bc_num *num, size_t scale)
96
137
bc_multiply_ex (guess , point5 , & guess , cscale );
97
138
bc_sub_ex (guess , guess1 , & diff , cscale + 1 );
98
139
if (bc_is_near_zero (diff , cscale )) {
99
- if (cscale < rscale + 1 ) {
100
- cscale = MIN (cscale * 3 , rscale + 1 );
140
+ if (cscale < scale + 1 ) {
141
+ cscale = MIN (cscale * 3 , scale + 1 );
101
142
} else {
102
143
done = true;
103
144
}
@@ -106,10 +147,59 @@ bool bc_sqrt(bc_num *num, size_t scale)
106
147
107
148
/* Assign the number and clean up. */
108
149
bc_free_num (num );
109
- bc_divide (guess , BCG (_one_ ), num , rscale );
150
+ bc_divide (guess , BCG (_one_ ), num , scale );
110
151
bc_free_num (& guess );
111
152
bc_free_num (& guess1 );
112
153
bc_free_num (& point5 );
113
154
bc_free_num (& diff );
155
+ }
156
+
157
+ bool bc_sqrt (bc_num * num , size_t scale )
158
+ {
159
+ /* Initial checks. */
160
+ if (bc_is_neg (* num )) {
161
+ /* Cannot take the square root of a negative number */
162
+ return false;
163
+ }
164
+
165
+ size_t num_calc_scale = (scale + 1 ) * 2 ;
166
+ size_t num_use_scale = MIN ((* num )-> n_scale , num_calc_scale );
167
+
168
+ /* Square root of 0 is 0 */
169
+ if (bc_is_zero_for_scale (* num , num_use_scale )) {
170
+ bc_free_num (num );
171
+ * num = bc_copy_num (BCG (_zero_ ));
172
+ return true;
173
+ }
174
+
175
+ bcmath_compare_result num_cmp_one = bc_compare (* num , BCG (_one_ ), num_use_scale );
176
+ /* Square root of 1 is 1 */
177
+ if (num_cmp_one == BCMATH_EQUAL ) {
178
+ bc_free_num (num );
179
+ * num = bc_copy_num (BCG (_one_ ));
180
+ return true;
181
+ }
182
+
183
+ /* Initialize the variables. */
184
+ size_t leading_zeros = 0 ;
185
+ size_t num_calc_full_len = (* num )-> n_len + num_calc_scale ;
186
+ size_t num_use_full_len = (* num )-> n_len + num_use_scale ;
187
+ if (num_cmp_one == BCMATH_RIGHT_GREATER ) {
188
+ const char * nptr = (* num )-> n_value ;
189
+ while (* nptr == 0 ) {
190
+ leading_zeros ++ ;
191
+ nptr ++ ;
192
+ }
193
+ num_calc_full_len -= leading_zeros ;
194
+ num_use_full_len -= leading_zeros ;
195
+ }
196
+
197
+ /* Find the square root using Newton's algorithm. */
198
+ if (num_calc_full_len < MAX_LENGTH_OF_LONG ) {
199
+ bc_fast_sqrt (num , scale , num_calc_full_len , num_use_full_len , leading_zeros );
200
+ } else {
201
+ bc_standard_sqrt (num , scale , num_cmp_one );
202
+ }
203
+
114
204
return true;
115
205
}
0 commit comments