3232#include "bcmath.h"
3333#include <stdbool.h>
3434#include <stddef.h>
35+ #include "private.h"
3536
3637/* Take the square root NUM and return it in NUM with SCALE digits
3738 after the decimal place. */
3839
39- bool bc_sqrt ( bc_num * num , size_t scale )
40+ static inline BC_VECTOR bc_sqrt_get_pow_10 ( size_t exponent )
4041{
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 ;
4546 }
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 ;
5189 }
5290
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 ;
5999 }
100+ bc_free_num (num );
101+ * num = ret ;
102+ }
60103
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+ {
63106 bc_num guess ;
64- size_t rscale = MAX (scale , (* num )-> n_scale );
65-
107+ size_t cscale ;
66108 /* Calculate the initial guess. */
67109 if (num_cmp_one == BCMATH_RIGHT_GREATER ) {
68110 /* 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)
86128 point5 -> n_value [1 ] = 5 ;
87129 bc_num diff = NULL ;
88130
89- /* Find the square root using Newton's algorithm. */
90131 bool done = false;
91132 while (!done ) {
92133 bc_free_num (& guess1 );
@@ -96,8 +137,8 @@ bool bc_sqrt(bc_num *num, size_t scale)
96137 bc_multiply_ex (guess , point5 , & guess , cscale );
97138 bc_sub_ex (guess , guess1 , & diff , cscale + 1 );
98139 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 );
101142 } else {
102143 done = true;
103144 }
@@ -106,10 +147,59 @@ bool bc_sqrt(bc_num *num, size_t scale)
106147
107148 /* Assign the number and clean up. */
108149 bc_free_num (num );
109- bc_divide (guess , BCG (_one_ ), num , rscale );
150+ bc_divide (guess , BCG (_one_ ), num , scale );
110151 bc_free_num (& guess );
111152 bc_free_num (& guess1 );
112153 bc_free_num (& point5 );
113154 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+
114204 return true;
115205}
0 commit comments