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
39
- bool bc_sqrt ( bc_num * num , size_t scale )
40
+ static inline BC_VECTOR bc_sqrt_get_pow_10 ( size_t exponent )
40
41
{
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 ;
51
46
}
47
+ value *= BC_POW_10_LUT [exponent ];
48
+ return value ;
49
+ }
52
50
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 ];
59
82
}
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
+ }
60
113
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
+ {
63
116
bc_num guess ;
64
- size_t rscale = MAX (scale , (* num )-> n_scale );
65
-
117
+ size_t cscale ;
66
118
/* Calculate the initial guess. */
67
119
if (num_cmp_one == BCMATH_RIGHT_GREATER ) {
68
120
/* The number is between 0 and 1. Guess should start at 1. */
@@ -84,7 +136,6 @@ bool bc_sqrt(bc_num *num, size_t scale)
84
136
point5 -> n_value [1 ] = 5 ;
85
137
bc_num diff = NULL ;
86
138
87
- /* Find the square root using Newton's algorithm. */
88
139
bool done = false;
89
140
while (!done ) {
90
141
bc_free_num (& guess1 );
@@ -109,5 +160,40 @@ bool bc_sqrt(bc_num *num, size_t scale)
109
160
bc_free_num (& guess1 );
110
161
bc_free_num (& point5 );
111
162
bc_free_num (& diff );
163
+ }
164
+
165
+ bool bc_sqrt (bc_num * num , size_t scale )
166
+ {
167
+ /* Initial checks. */
168
+ if (bc_is_neg (* num )) {
169
+ /* Cannot take the square root of a negative number */
170
+ return false;
171
+ }
172
+ /* Square root of 0 is 0 */
173
+ if (bc_is_zero (* num )) {
174
+ bc_free_num (num );
175
+ * num = bc_copy_num (BCG (_zero_ ));
176
+ return true;
177
+ }
178
+
179
+ bcmath_compare_result num_cmp_one = bc_compare (* num , BCG (_one_ ), (* num )-> n_scale );
180
+ /* Square root of 1 is 1 */
181
+ if (num_cmp_one == BCMATH_EQUAL ) {
182
+ bc_free_num (num );
183
+ * num = bc_copy_num (BCG (_one_ ));
184
+ return true;
185
+ }
186
+
187
+ /* Initialize the variables. */
188
+ size_t rscale = MAX (scale , (* num )-> n_scale );
189
+ size_t num_calc_full_len = (* num )-> n_len + (rscale + 1 ) * 2 ;
190
+
191
+ /* Find the square root using Newton's algorithm. */
192
+ if (num_calc_full_len < MAX_LENGTH_OF_LONG ) {
193
+ bc_fast_sqrt (num , rscale );
194
+ } else {
195
+ bc_standard_sqrt (num , rscale , num_cmp_one );
196
+ }
197
+
112
198
return true;
113
199
}
0 commit comments