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
40+ static inline BC_VECTOR bc_sqrt_get_pow_10 (size_t exponent )
41+ {
42+ BC_VECTOR value = 1 ;
43+ while (exponent >= 8 ) {
44+ value *= BC_POW_10_LUT [8 ];
45+ exponent -= 8 ;
46+ }
47+ value *= BC_POW_10_LUT [exponent ];
48+ return value ;
49+ }
50+
3951bool bc_sqrt (bc_num * num , size_t scale )
4052{
4153 /* Initial checks. */
@@ -59,55 +71,108 @@ bool bc_sqrt(bc_num *num, size_t scale)
5971 }
6072
6173 /* Initialize the variables. */
62- size_t cscale ;
63- bc_num guess ;
6474 size_t rscale = MAX (scale , (* num )-> n_scale );
6575
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 */
8179
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+ }
8685
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 ) {
100102 done = true;
101103 }
102104 }
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 );
103175 }
104176
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 );
112177 return true;
113178}
0 commit comments