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 rscale , size_t num_calc_full_len , size_t leading_zeros )
68+ {
69+ BC_VECTOR n_vector = 0 ;
70+ for (size_t i = leading_zeros ; i < (* num )-> n_len + (* num )-> n_scale ; i ++ ) {
71+ n_vector = n_vector * BASE + (* num )-> n_value [i ];
72+ }
73+ /* When calculating the square root of a number using only integer operations,
74+ * need to adjust the digit scale accordingly.
75+ * Considering that the original number is the square of the result,
76+ * if the desired scale of the result is 5, the input number should be scaled
77+ * by twice that, i.e., scale 10. */
78+ n_vector *= bc_sqrt_get_pow_10 ((rscale + 1 ) * 2 - (* num )-> n_scale );
79+
80+ /* Get sqrt */
81+ BC_VECTOR guess_vector = bc_fast_sqrt_vector (n_vector );
82+
83+ size_t ret_len ;
84+ if (leading_zeros > 0 ) {
85+ ret_len = 1 ;
86+ } else {
87+ ret_len = ((num_calc_full_len + 1 ) / 2 ) - (rscale + 1 );
5188 }
5289
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;
90+ bc_num ret = bc_new_num_nonzeroed (ret_len , rscale );
91+ char * rptr = ret -> n_value ;
92+ char * rend = rptr + ret_len + rscale - 1 ;
93+
94+ guess_vector /= BASE ; /* Since the scale of guess_vector is rscale + 1, reduce the scale by 1. */
95+ while (rend >= rptr ) {
96+ * rend -- = guess_vector % BASE ;
97+ guess_vector /= BASE ;
5998 }
99+ bc_free_num (num );
100+ * num = ret ;
101+ }
60102
61- /* Initialize the variables. */
62- size_t cscale ;
103+ static inline void bc_standard_sqrt ( bc_num * num , size_t rscale , bcmath_compare_result num_cmp_one )
104+ {
63105 bc_num guess ;
64- size_t rscale = MAX (scale , (* num )-> n_scale );
65-
106+ size_t cscale ;
66107 /* Calculate the initial guess. */
67108 if (num_cmp_one == BCMATH_RIGHT_GREATER ) {
68109 /* The number is between 0 and 1. Guess should start at 1. */
@@ -86,7 +127,6 @@ bool bc_sqrt(bc_num *num, size_t scale)
86127 point5 -> n_value [1 ] = 5 ;
87128 bc_num diff = NULL ;
88129
89- /* Find the square root using Newton's algorithm. */
90130 bool done = false;
91131 while (!done ) {
92132 bc_free_num (& guess1 );
@@ -111,5 +151,53 @@ bool bc_sqrt(bc_num *num, size_t scale)
111151 bc_free_num (& guess1 );
112152 bc_free_num (& point5 );
113153 bc_free_num (& diff );
154+ }
155+
156+ bool bc_sqrt (bc_num * num , size_t scale )
157+ {
158+ /* Initial checks. */
159+ if (bc_is_neg (* num )) {
160+ /* Cannot take the square root of a negative number */
161+ return false;
162+ }
163+ /* Square root of 0 is 0 */
164+ if (bc_is_zero (* num )) {
165+ bc_free_num (num );
166+ * num = bc_copy_num (BCG (_zero_ ));
167+ return true;
168+ }
169+
170+ bcmath_compare_result num_cmp_one = bc_compare (* num , BCG (_one_ ), (* num )-> n_scale );
171+ /* Square root of 1 is 1 */
172+ if (num_cmp_one == BCMATH_EQUAL ) {
173+ bc_free_num (num );
174+ * num = bc_copy_num (BCG (_one_ ));
175+ return true;
176+ }
177+
178+ /* Initialize the variables. */
179+ size_t rscale = MAX (scale , (* num )-> n_scale );
180+ size_t leading_zeros = 0 ;
181+ size_t num_calc_full_len ;
182+ if (num_cmp_one == BCMATH_RIGHT_GREATER ) {
183+ const char * nptr = (* num )-> n_value ;
184+ /* We know that num is not zero, so there will be no overrun. */
185+ while (* nptr == 0 ) {
186+ leading_zeros ++ ;
187+ nptr ++ ;
188+ }
189+ size_t leading_zeros_in_frac = leading_zeros - 1 ;
190+ num_calc_full_len = (rscale + 1 - leading_zeros_in_frac ) * 2 ;
191+ } else {
192+ num_calc_full_len = (* num )-> n_len + (rscale + 1 ) * 2 ;
193+ }
194+
195+ /* Find the square root using Newton's algorithm. */
196+ if (num_calc_full_len < MAX_LENGTH_OF_LONG ) {
197+ bc_fast_sqrt (num , rscale , num_calc_full_len , leading_zeros );
198+ } else {
199+ bc_standard_sqrt (num , rscale , num_cmp_one );
200+ }
201+
114202 return true;
115203}
0 commit comments