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;
45- }
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;
42+ BC_VECTOR value = 1 ;
43+ while (exponent >= 8 ) {
44+ value *= BC_POW_10_LUT [8 ];
45+ exponent -= 8 ;
5146 }
47+ value *= BC_POW_10_LUT [exponent ];
48+ return value ;
49+ }
5250
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;
51+ static BC_VECTOR bc_fast_sqrt_vector (BC_VECTOR n_vector )
52+ {
53+ /* Use a bitwise method for approximating the square root
54+ * as the initial guess for Newton's method. */
55+ union {
56+ uint64_t i ;
57+ double d ;
58+ } u ;
59+ u .d = (double ) n_vector ;
60+ u .i = (1ULL << 61 ) + (u .i >> 1 ) - (1ULL << 50 );
61+ BC_VECTOR guess_vector = (BC_VECTOR ) u .d ;
62+
63+ /* Newton's algorithm. */
64+ BC_VECTOR guess1_vector ;
65+ size_t diff ;
66+ do {
67+ guess1_vector = guess_vector ;
68+ guess_vector = n_vector / guess_vector ;
69+ guess_vector += guess1_vector ;
70+ guess_vector /= 2 ;
71+ diff = guess1_vector > guess_vector ? guess1_vector - guess_vector : guess_vector - guess1_vector ;
72+ } while (diff > 1 );
73+ return guess_vector ;
74+ }
75+
76+ static inline void bc_fast_sqrt (bc_num * num , size_t rscale )
77+ {
78+ BC_VECTOR n_vector = 0 ;
79+ size_t i = 0 ;
80+ for (; i < (* num )-> n_len + (* num )-> n_scale ; i ++ ) {
81+ n_vector = n_vector * BASE + (* num )-> n_value [i ];
5982 }
83+ /* When calculating the square root of a number using only integer operations,
84+ * need to adjust the digit scale accordingly.
85+ * Considering that the original number is the square of the result,
86+ * if the desired scale of the result is 5, the input number should be scaled
87+ * by twice that, i.e., scale 10. */
88+ n_vector *= bc_sqrt_get_pow_10 ((rscale + 1 ) * 2 - (* num )-> n_scale );
89+
90+ /* Get sqrt */
91+ BC_VECTOR guess_vector = bc_fast_sqrt_vector (n_vector );
92+
93+ size_t full_len = 0 ;
94+ BC_VECTOR tmp_guess_vector = guess_vector ;
95+ do {
96+ tmp_guess_vector /= BASE ;
97+ full_len ++ ;
98+ } while (tmp_guess_vector > 0 );
99+
100+ size_t ret_ren = full_len > rscale + 1 ? full_len - (rscale + 1 ) : 1 ; /* for int zero */
101+ bc_num ret = bc_new_num_nonzeroed (ret_ren , rscale );
102+ char * rptr = ret -> n_value ;
103+ char * rend = rptr + ret_ren + rscale - 1 ;
104+
105+ guess_vector /= BASE ; /* Since the scale of guess_vector is rscale + 1, reduce the scale by 1. */
106+ while (rend >= rptr ) {
107+ * rend -- = guess_vector % BASE ;
108+ guess_vector /= BASE ;
109+ }
110+ bc_free_num (num );
111+ * num = ret ;
112+ }
60113
61- /* Initialize the variables. */
62- size_t cscale ;
114+ static inline void bc_standard_sqrt ( bc_num * num , size_t rscale , bcmath_compare_result num_cmp_one )
115+ {
63116 bc_num guess ;
64- size_t rscale = MAX (scale , (* num )-> n_scale );
65-
117+ size_t cscale ;
66118 /* Calculate the initial guess. */
67119 if (num_cmp_one == BCMATH_RIGHT_GREATER ) {
68120 /* The number is between 0 and 1. Guess should start at 1. */
@@ -86,7 +138,6 @@ bool bc_sqrt(bc_num *num, size_t scale)
86138 point5 -> n_value [1 ] = 5 ;
87139 bc_num diff = NULL ;
88140
89- /* Find the square root using Newton's algorithm. */
90141 bool done = false;
91142 while (!done ) {
92143 bc_free_num (& guess1 );
@@ -111,5 +162,40 @@ bool bc_sqrt(bc_num *num, size_t scale)
111162 bc_free_num (& guess1 );
112163 bc_free_num (& point5 );
113164 bc_free_num (& diff );
165+ }
166+
167+ bool bc_sqrt (bc_num * num , size_t scale )
168+ {
169+ /* Initial checks. */
170+ if (bc_is_neg (* num )) {
171+ /* Cannot take the square root of a negative number */
172+ return false;
173+ }
174+ /* Square root of 0 is 0 */
175+ if (bc_is_zero (* num )) {
176+ bc_free_num (num );
177+ * num = bc_copy_num (BCG (_zero_ ));
178+ return true;
179+ }
180+
181+ bcmath_compare_result num_cmp_one = bc_compare (* num , BCG (_one_ ), (* num )-> n_scale );
182+ /* Square root of 1 is 1 */
183+ if (num_cmp_one == BCMATH_EQUAL ) {
184+ bc_free_num (num );
185+ * num = bc_copy_num (BCG (_one_ ));
186+ return true;
187+ }
188+
189+ /* Initialize the variables. */
190+ size_t rscale = MAX (scale , (* num )-> n_scale );
191+ size_t num_calc_full_len = (* num )-> n_len + (rscale + 1 ) * 2 ;
192+
193+ /* Find the square root using Newton's algorithm. */
194+ if (num_calc_full_len < MAX_LENGTH_OF_LONG ) {
195+ bc_fast_sqrt (num , rscale );
196+ } else {
197+ bc_standard_sqrt (num , rscale , num_cmp_one );
198+ }
199+
114200 return true;
115201}
0 commit comments