1use crate::*;
4use super::exp2;
5use arithmetic::decimal::get_power_of_ten_u64;
6
7pub(crate) fn impl_inverse_uint_scale(n: &BigUint, scale: i64, ctx: &Context) -> BigDecimal {
9
10 if let Some(small_pow_ten) = n.to_u64().and_then(get_power_of_ten_u64) {
11 let prec = ctx.precision().get();
16 let inv_int = BigInt::from(10u8).pow(prec as u32 - 1);
17
18 let inv_scale = small_pow_ten as i64 - scale + prec as i64 - 1;
20
21 return BigDecimal::new(inv_int, inv_scale);
22 }
23
24 let guess = n.to_f64()
26 .filter(|f| f.is_normal())
27 .map(|f| 1.0 / f)
28 .filter(|&f| f != 0.0 && f.is_finite())
29 .and_then(BigDecimal::from_f64)
30 .map(|mut d| { d.scale -= scale; d })
31 .unwrap_or_else(
32 || make_inv_guess(n.bits(), scale));
34
35 let max_precision = ctx.precision().get();
36
37 let s = BigDecimal::new(BigInt::from_biguint(Sign::Plus, n.clone()), scale);
38 let two = BigDecimal::from(2);
39
40 let next_iteration = move |r: BigDecimal| {
41 let tmp = &two - &s * &r;
42 r * tmp
43 };
44
45 let mut running_result = next_iteration(guess);
47 if true {
if !!running_result.is_zero() {
{
::std::rt::panic_fmt(format_args!("Zero detected in inverse calculation of {0}e{1}",
n, -scale));
}
};
};debug_assert!(!running_result.is_zero(), "Zero detected in inverse calculation of {}e{}", n, -scale);
48
49 let mut prev_result = BigDecimal::one();
50 let mut result = BigDecimal::zero();
51
52 while prev_result != result {
55 prev_result = result;
57
58 running_result = next_iteration(running_result).with_prec(max_precision + 2);
60
61 result = if running_result.digits() > max_precision {
63 running_result.with_precision_round(ctx.precision(), ctx.rounding_mode())
64 } else {
65 running_result.clone()
66 };
67 }
68
69 return result;
70}
71
72
73fn make_inv_guess(bit_count: u64, scale: i64) -> BigDecimal {
75 let magic_factor = stdlib::f64::consts::LN_2;
77
78 let bit_count = bit_count as f64;
79 let initial_guess = magic_factor * exp2(-bit_count);
80 if initial_guess.is_finite() && initial_guess != 0.0 {
81 if let Ok(mut result) = BigDecimal::try_from(initial_guess) {
82 result.scale -= scale;
83 return result;
84 }
85 }
86
87 let approx_scale = bit_count * stdlib::f64::consts::LOG10_2;
90 let approx_scale_int = approx_scale.trunc();
91 let approx_scale_frac = approx_scale - approx_scale_int;
92
93 let recip = libm::exp10(-approx_scale_frac);
94 let mut res = BigDecimal::from_f32((magic_factor * recip) as f32).unwrap();
95 res.scale += approx_scale_int as i64;
96 res.scale -= scale;
97 return res;
98}
99
100
101#[cfg(test)]
102mod test_make_inv_guess {
103 use super::*;
104 use paste::paste;
105
106 macro_rules! impl_case {
107 ( $bin_count:literal, -$scale:literal => $expected:literal ) => {
108 paste! { impl_case!( [< case_ $bin_count _n $scale >]: $bin_count, -$scale => $expected); }
109 };
110 ( $bin_count:literal, $scale:literal => $expected:literal ) => {
111 paste! { impl_case!( [< case_ $bin_count _ $scale >]: $bin_count, $scale => $expected); }
112 };
113 ( $name:ident: $bin_count:expr, $scale:expr => $expected:literal ) => {
114 impl_case!($name: $bin_count, $scale, prec=5 => $expected);
115 };
116 ( $name:ident: $bin_count:expr, $scale:expr, prec=$prec:literal => $expected:literal ) => {
117 #[test]
118 fn $name() {
119 let guess = make_inv_guess($bin_count, $scale);
120 let expected: BigDecimal = $expected.parse().unwrap();
121 assert_eq!(guess.with_prec($prec), expected.with_prec($prec));
122 }
123 };
124 }
125
126 impl_case!(0, 0 => "0.69315");
127 impl_case!(1, 0 => "0.34657");
128 impl_case!(2, 0 => "0.17329");
129 impl_case!(2, 1 => "1.7329");
130
131 impl_case!(3, -5 => "8.6643e-07");
133
134 impl_case!(20, 0 => "6.6104e-07");
136 impl_case!(20, -900 => "6.6104E-907");
137 impl_case!(20, 800 => "6.6104E+793");
138
139 impl_case!(40, 10000 => "6.3041E+9987");
140
141 impl_case!(70, -5 => "5.8712e-27");
142 impl_case!(70, 5 => "5.8712e-17");
143 impl_case!(70, 50 => "5.8712e+28");
144
145 impl_case!(888, -300 => "3.3588E-568");
146 impl_case!(888, -19 => "3.3588E-287");
147 impl_case!(888, 0 => "3.3588E-268");
148 impl_case!(888, 270 => "335.88");
149
150 impl_case!(1022, 10 => "1.5423e-298");
151 impl_case!(1022, 308 => "1.5423");
152
153 impl_case!(1038, 316 => "2353.4");
154
155 impl_case!(case_31028_n659: 31028, -659 => "3.0347E-10000");
156 impl_case!(case_31028_0: 31028, 0 => "3.0347E-9341");
157 impl_case!(case_31028_1: 31028, 1 => "3.0347E-9340");
158 impl_case!(case_31028_9340: 31028, 9340 => ".30347");
159 impl_case!(case_31028_10000: 31028, 10000 => "3.0347E+659");
160
161 }
163
164#[cfg(test)]
165mod test {
166 use super::*;
167 use paste::paste;
168 use stdlib::num::NonZeroU64;
169
170 #[test]
171 fn test_inverse_35543972957198043e291() {
172 let v = vec![
173 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
174 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
175 2324389888, 849200558
176 ];
177 let x = BigInt::new(Sign::Minus, v);
178 let d = BigDecimal::from(x);
179 let expected = "-2.813416500187520746852694701086705659180043761702417561798711758892800449936819185796527214192677476E-308".parse::<BigDecimal>().unwrap();
180 assert_eq!(d.inverse(), expected);
181
182 assert_eq!(d.neg().inverse(), expected.neg());
183 }
184
185 macro_rules! impl_case {
186 ($name:ident: $prec:literal, $round:ident => $expected:literal) => {
187 #[test]
188 fn $name() {
189 let n = test_input();
190 let prec = NonZeroU64::new($prec).unwrap();
191 let rounding = RoundingMode::$round;
192 let ctx = Context::new(prec, rounding);
193
194 let result = n.inverse_with_context(&ctx);
195
196 let expected = $expected.parse::<BigDecimal>().unwrap();
197 assert_eq!(&result, &expected);
198
199 let product = result * &n;
200 let epsilon = BigDecimal::new(BigInt::one(), $prec - 1);
201 let diff = (BigDecimal::one() - &product).abs();
202 assert!(diff < epsilon);
203 }
204 };
205 (prec=$prec:literal, round=$round:ident => $expected:literal) => {
206 paste! {
207 #[test]
208 fn [< case_prec $prec _round_ $round:lower >] () {
209 let n = test_input();
210 let prec = NonZeroU64::new($prec).unwrap();
211 let rounding = RoundingMode::$round;
212 let ctx = Context::new(prec, rounding);
213
214 let result = n.inverse_with_context(&ctx);
215
216 let expected = $expected.parse::<BigDecimal>().unwrap();
217 assert_eq!(&result, &expected);
218 assert_eq!(&result.scale, &expected.scale);
219 }
220 }
221 };
222 (prec=$prec:literal, round=$($round:ident),+ => $expected:literal) => {
223 $( impl_case!(prec=$prec, round=$round => $expected); )*
224 };
225 }
226
227 mod invert_one {
228 use super::*;
229
230 fn test_input() -> BigDecimal {
231 1u8.into()
232 }
233
234 impl_case!(prec=1, round=Up,Down => "1");
235 impl_case!(prec=2, round=Up,Down => "1.0");
236 impl_case!(prec=7, round=Up,Down => "1.000000");
237 }
238
239 mod invert_n1d00 {
240 use super::*;
241
242 fn test_input() -> BigDecimal {
243 "-1.00".parse().unwrap()
244 }
245
246 impl_case!(prec=1, round=Up,Down => "-1");
247 impl_case!(prec=5, round=Up,Down => "-1.0000");
248 }
249
250 mod invert_n1000en8 {
251 use super::*;
252
253 fn test_input() -> BigDecimal {
254 "1000e-8".parse().unwrap()
255 }
256
257 impl_case!(prec=1, round=Up,Down => "1e5");
258 impl_case!(prec=5, round=Up,Down => "10000e1");
259 impl_case!(prec=6, round=Up,Down => "100000");
260 impl_case!(prec=8, round=Up,Down => "100000.00");
261 }
262
263 mod invert_seven {
264 use super::*;
265
266 fn test_input() -> BigDecimal {
267 BigDecimal::from(7u8)
268 }
269
270 impl_case!(case_prec10_round_down: 10, Down => "0.1428571428");
271 impl_case!(case_prec10_round_up: 10, Up => "0.1428571429");
272
273 impl_case!(case_prec11_round_ceiling: 11, Ceiling => "0.14285714286");
274 }
275
276 mod invert_ten {
277 use super::*;
278
279 fn test_input() -> BigDecimal {
280 10u8.into()
281 }
282
283 impl_case!(case_prec1_round_down: 1, Down => "0.1");
284 impl_case!(case_prec2_round_down: 2, Down => "0.10");
285 impl_case!(prec=10, round=Up, Down => "0.1000000000");
286 }
287
288 mod invert_n3242342d34324 {
289 use super::*;
290
291 fn test_input() -> BigDecimal {
292 "-3242342.34324".parse().unwrap()
293 }
294
295 impl_case!(prec=50, round=Up, Ceiling => "-3.0841900519385698894827476971712670726697831310897E-7");
297 impl_case!(prec=50, round=Down, Floor => "-3.0841900519385698894827476971712670726697831310896E-7");
298 }
299
300
301 mod invert_2d8722377233432854650en126 {
302 use super::*;
303
304 fn test_input() -> BigDecimal {
305 "28722377233432854650456573411382289859440620032075590707304700193738855195818029876568741547799767753181511758371393266031229989006058870578446812747289276920741036671713994469786904880406812933015496296559493964954240161851051500623562557032166800306346000498803201936493334049050141321136859175463065287081665388768669799901545047760009765625e-469"
306 .parse().unwrap()
307 }
308
309 impl_case!(prec=1, round=Up => "4e125");
310 impl_case!(prec=5, round=Up => "3.4817e+125");
311 impl_case!(prec=25, round=Up => "3.481605968311006434080812E+125");
312 }
313
314 #[test]
315 fn inv_random_number() {
316 let n = BigDecimal::try_from(0.08121970592310568).unwrap();
317
318 let ctx = Context::new(NonZeroU64::new(40).unwrap(), RoundingMode::Down);
319 let i = n.inverse_with_context(&ctx);
320 assert_eq!(&i, &"12.31228294456944530942557443718279245563".parse::<BigDecimal>().unwrap());
321
322 let product = i * &n;
323 assert!(BigDecimal::one() - &product < "1e-39".parse::<BigDecimal>().unwrap());
324 }
325
326 #[cfg(property_tests)]
327 mod prop {
328 use super::*;
329 use proptest::*;
330 use num_traits::FromPrimitive;
331
332 proptest! {
333
334 #[test]
335 fn inverse_multiplies_to_one(f: f64, prec in 1..100u64) {
336 prop_assume!(f.is_normal());
338 prop_assume!(f != 0.0);
339
340 let n = BigDecimal::from_f64(f).unwrap();
341
342 let ctx = Context::new(NonZeroU64::new(prec).unwrap(), RoundingMode::Up);
343 let i = n.inverse_with_context(&ctx);
344 let product = &i * &n;
345
346 let epsilon = BigDecimal::new(1.into(), prec as i64 - 1);
348 let diff_from_one = BigDecimal::one() - &product;
349
350 prop_assert!(diff_from_one.abs() < epsilon, "{} >= {}", diff_from_one.abs(), epsilon);
351 }
352 }
353 }
354}