bigdecimal/arithmetic/sqrt.rs
1//! square root implementation
2
3use crate::*;
4
5
6pub(crate) fn impl_sqrt(n: &BigUint, scale: i64, ctx: &Context) -> BigDecimal {
7 // Calculate the number of digits and the difference compared to the scale
8 let num_digits = count_decimal_digits_uint(n);
9 let scale_diff = BigInt::from(num_digits) - scale;
10
11 // Calculate the number of wanted digits and the exponent we need to raise the original value to
12 // We want twice as many digits as the precision because sqrt halves the number of digits
13 // We add an extra one for rounding purposes
14 let prec = ctx.precision().get();
15 let extra_rounding_digit_count = 5;
16 let wanted_digits = 2 * (prec + extra_rounding_digit_count);
17 let base_exponent = wanted_digits.saturating_sub(num_digits);
18 let exponent = base_exponent + ((scale as u64 ^ base_exponent) & 1);
19 let sqrt_digits = (n * ten_to_the_uint(exponent)).sqrt();
20
21 // Calculate the scale of the result
22 let result_scale_digits = 2 * (2 * prec - scale_diff) - 1;
23 let result_scale_decimal: BigDecimal = BigDecimal::new(result_scale_digits, 0) / 4.0;
24 let mut result_scale = result_scale_decimal.with_scale_round(0, RoundingMode::HalfEven).int_val;
25
26 // Round the value so it has the correct precision requested
27 result_scale += count_decimal_digits_uint(&sqrt_digits).saturating_sub(prec);
28 let unrounded_result = BigDecimal::new(sqrt_digits.into(), result_scale.to_i64().unwrap());
29 unrounded_result.with_precision_round(ctx.precision(), ctx.rounding_mode())
30}
31
32#[cfg(test)]
33mod test {
34 use super::*;
35
36 include!("sqrt.tests.rs");
37}