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