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
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
+
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
+
39
76
bool bc_sqrt (bc_num * num , size_t scale )
40
77
{
41
78
/* Initial checks. */
@@ -59,55 +96,98 @@ bool bc_sqrt(bc_num *num, size_t scale)
59
96
}
60
97
61
98
/* Initialize the variables. */
62
- size_t cscale ;
63
- bc_num guess ;
64
99
size_t rscale = MAX (scale , (* num )-> n_scale );
100
+ size_t num_calc_full_len = (* num )-> n_len + (rscale + 1 ) * 2 ;
65
101
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
- }
102
+ /* Find the square root using Newton's algorithm. */
103
+ if (num_calc_full_len < MAX_LENGTH_OF_LONG ) {
104
+ /* Fast path */
81
105
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 ;
106
+ BC_VECTOR n_vector = 0 ;
107
+ size_t i = 0 ;
108
+ for (; i < (* num )-> n_len + (* num )-> n_scale ; i ++ ) {
109
+ n_vector = n_vector * BASE + (* num )-> n_value [i ];
110
+ }
111
+ /* When calculating the square root of a number using only integer operations,
112
+ * need to adjust the digit scale accordingly.
113
+ * Considering that the original number is the square of the result,
114
+ * if the desired scale of the result is 5, the input number should be scaled
115
+ * by twice that, i.e., scale 10. */
116
+ n_vector *= bc_sqrt_get_pow_10 ((rscale + 1 ) * 2 - (* num )-> n_scale );
117
+
118
+ /* Get sqrt */
119
+ BC_VECTOR guess_vector = bc_fast_sqrt_vector (n_vector );
120
+
121
+ size_t full_len = 0 ;
122
+ BC_VECTOR tmp_guess_vector = guess_vector ;
123
+ do {
124
+ tmp_guess_vector /= BASE ;
125
+ full_len ++ ;
126
+ } while (tmp_guess_vector > 0 );
127
+
128
+ size_t ret_ren = full_len > rscale + 1 ? full_len - (rscale + 1 ) : 1 ; /* for int zero */
129
+ bc_num ret = bc_new_num_nonzeroed (ret_ren , rscale );
130
+ char * rptr = ret -> n_value ;
131
+ char * rend = rptr + ret_ren + rscale - 1 ;
132
+
133
+ guess_vector /= BASE ; /* Since the scale of guess_vector is rscale + 1, reduce the scale by 1. */
134
+ while (rend >= rptr ) {
135
+ * rend -- = guess_vector % BASE ;
136
+ guess_vector /= BASE ;
137
+ }
138
+ bc_free_num (num );
139
+ * num = ret ;
140
+ } else {
141
+ /* Standard path */
142
+
143
+ bc_num guess ;
144
+ size_t cscale ;
145
+ /* Calculate the initial guess. */
146
+ if (num_cmp_one == BCMATH_RIGHT_GREATER ) {
147
+ /* The number is between 0 and 1. Guess should start at 1. */
148
+ guess = bc_copy_num (BCG (_one_ ));
149
+ cscale = (* num )-> n_scale ;
150
+ } else {
151
+ /* The number is greater than 1. Guess should start at 10^(exp/2). */
152
+ /* If just divide size_t by 2 it will not overflow. */
153
+ size_t exponent_for_initial_guess = (size_t ) (* num )-> n_len >> 1 ;
154
+
155
+ /* 10^n is a 1 followed by n zeros. */
156
+ guess = bc_new_num (exponent_for_initial_guess + 1 , 0 );
157
+ guess -> n_value [0 ] = 1 ;
158
+ cscale = 3 ;
159
+ }
86
160
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 {
100
- done = true;
161
+ bc_num guess1 = NULL ;
162
+ bc_num point5 = bc_new_num (1 , 1 );
163
+ point5 -> n_value [1 ] = 5 ;
164
+ bc_num diff = NULL ;
165
+
166
+ bool done = false;
167
+ while (!done ) {
168
+ bc_free_num (& guess1 );
169
+ guess1 = bc_copy_num (guess );
170
+ bc_divide (* num , guess , & guess , cscale );
171
+ bc_add_ex (guess , guess1 , & guess , 0 );
172
+ bc_multiply_ex (guess , point5 , & guess , cscale );
173
+ bc_sub_ex (guess , guess1 , & diff , cscale + 1 );
174
+ if (bc_is_near_zero (diff , cscale )) {
175
+ if (cscale < rscale + 1 ) {
176
+ cscale = MIN (cscale * 3 , rscale + 1 );
177
+ } else {
178
+ done = true;
179
+ }
101
180
}
102
181
}
182
+
183
+ /* Assign the number and clean up. */
184
+ bc_free_num (num );
185
+ bc_divide (guess , BCG (_one_ ), num , rscale );
186
+ bc_free_num (& guess );
187
+ bc_free_num (& guess1 );
188
+ bc_free_num (& point5 );
189
+ bc_free_num (& diff );
103
190
}
104
191
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 );
112
192
return true;
113
193
}
0 commit comments