1use alloc::string::ToString;
11use core::{cmp::max, fmt, mem::MaybeUninit};
12
13use super::{
14 common::floor_log10_pow2,
15 formatted::{Formatted, Part},
16 powers_of_five::is_multiple_of_pow5,
17};
18use crate::{
19 big_uint::{HiLo, U128},
20 f256, BigUInt, DivRem, U256, U512,
21};
22
23#[inline(always)]
25#[allow(clippy::cast_sign_loss)]
26#[allow(clippy::cast_possible_truncation)]
27fn floor_log10_pow5(i: i32) -> i32 {
28 debug_assert!(i >= 0);
29 debug_assert!(i <= 262380);
30 ((i as u128 * 24592820711491) >> 45) as i32
31}
32
33#[inline(always)]
35#[allow(clippy::cast_sign_loss)]
36fn floor_log2_pow5(i: i32) -> i32 {
37 debug_assert!(i >= 0);
38 debug_assert!(i <= 225798);
39 ((i as u64 * 81695582054029) >> 45) as i32
40}
41
42#[inline(always)]
44fn ceil_log2_pow5(i: i32) -> i32 {
45 floor_log2_pow5(i) + 1
46}
47
48#[inline(always)]
49fn lookup_pow2_div_pow5(idx: usize) -> U512 {
50 let t = f256_pow2_div_pow5_lut::lookup_pow2_div_pow5(idx);
51 U512 {
52 hi: U256::new(t.0, t.1),
53 lo: U256::new(t.2, t.3),
54 }
55}
56
57#[inline(always)]
58fn lookup_pow5_div_pow2(idx: usize) -> U512 {
59 let t = f256_pow5_div_pow2_lut::lookup_pow5_div_pow2(idx);
60 U512 {
61 hi: U256::new(t.0, t.1),
62 lo: U256::new(t.2, t.3),
63 }
64}
65
66pub(crate) fn u256_truncating_mul_u512(x: &U256, y: &U512) -> U256 {
68 let mut carry = U128::ZERO;
69 let mut l = (U128::ZERO, U128::ZERO, U128::ZERO, U128::ZERO);
70 let mut h = (U128::ZERO, U128::ZERO, U128::ZERO, U128::ZERO);
71 (_, carry) = y.lo.lo.widening_mul(&x.lo);
72 (l.0, carry) = y.lo.hi.carrying_mul(&x.lo, &carry);
73 (l.1, carry) = y.hi.lo.carrying_mul(&x.lo, &carry);
74 (l.2, carry) = y.hi.hi.carrying_mul(&x.lo, &carry);
75 l.3 = carry;
76 (h.0, carry) = y.lo.lo.widening_mul(&x.hi);
77 (h.1, carry) = y.lo.hi.carrying_mul(&x.hi, &carry);
78 (h.2, carry) = y.hi.lo.carrying_mul(&x.hi, &carry);
79 (h.3, carry) = y.hi.hi.carrying_mul(&x.hi, &carry);
80 let mut hi = carry;
81 let (_, carry) = l.0.overflowing_add(&h.0);
82 let (_, carry) = l.1.carrying_add(&h.1, carry);
83 let (_, carry) = l.2.carrying_add(&h.2, carry);
84 let (lo, carry) = l.3.carrying_add(&h.3, carry);
85 hi.incr_if(carry);
86 U256::from_hi_lo(hi, lo)
87}
88
89#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
92pub(crate) struct DecNumRepr {
93 pub(crate) sign: u32,
94 pub(crate) exp10: i32,
95 pub(crate) signif10: U256,
96}
97
98impl DecNumRepr {
99 pub(crate) fn shortest_from_f256(f: &f256) -> Self {
102 debug_assert!(f.is_finite() && !f.eq_zero());
103
104 let sign = f.sign();
106 let exp2 = f.quantum_exponent();
107 let signif2 = f.integral_significand();
108
109 let (signif10, exp10) = Self::shortest_from_bin_repr(signif2, exp2);
111
112 Self {
113 sign,
114 exp10,
115 signif10,
116 }
117 }
118
119 #[allow(clippy::cast_sign_loss)]
122 #[allow(clippy::cast_possible_wrap)]
123 #[allow(clippy::cognitive_complexity)]
124 pub(crate) fn shortest_from_bin_repr(
125 mut signif2: U256,
126 mut exp2: i32,
127 ) -> (U256, i32) {
128 const H: i32 = 501;
135
136 let accept_bounds = signif2.is_even();
137
138 exp2 -= 2;
140 signif2 <<= 2;
141
142 let is_non_integer = exp2 < -(signif2.trailing_zeros() as i32);
145 let lower_signif2 = signif2 - U256::from(1 + is_non_integer as u128);
146 let upper_signif2 = signif2 + U256::TWO;
147
148 let mut exp10: i32;
150 let mut signif10 = U256::default();
151 let mut lower_signif10 = U256::default();
152 let mut upper_signif10 = U256::default();
153 let mut rem_zero = false;
154 let mut lower_rem_zero = false;
155 if exp2 >= 0 {
156 let g = floor_log10_pow2(exp2) - (exp2 > 3) as i32;
160 exp10 = g;
161 let h = floor_log2_pow5(g) + H;
164 let sh = (exp2 - g - h + 510) as u32;
165 let luv = lookup_pow2_div_pow5(g as usize);
166 lower_signif10 =
167 u256_truncating_mul_u512(&(lower_signif2 << sh), &luv);
168 signif10 = u256_truncating_mul_u512(&(signif2 << sh), &luv);
169 upper_signif10 =
170 u256_truncating_mul_u512(&(upper_signif2 << sh), &luv);
171 if g <= 102 {
178 if (signif2 % U128::from(5_u128)).is_zero() {
181 rem_zero = is_multiple_of_pow5(&signif2, g as u32);
182 } else if accept_bounds {
183 lower_rem_zero =
184 is_multiple_of_pow5(&lower_signif2, g as u32);
185 } else if is_multiple_of_pow5(&upper_signif2, g as u32) {
186 upper_signif10.decr();
187 }
188 }
189 } else {
190 let g = floor_log10_pow5(-exp2) - (exp2 != -1) as i32;
195 exp10 = exp2 + g;
196 let i = -exp2 - g;
197 let h = ceil_log2_pow5(i) - H;
201 let sh = (510 - g + h) as u32;
202 let luv = lookup_pow5_div_pow2(i as usize);
203 lower_signif10 =
204 u256_truncating_mul_u512(&(lower_signif2 << sh), &luv);
205 signif10 = u256_truncating_mul_u512(&(signif2 << sh), &luv);
206 upper_signif10 =
207 u256_truncating_mul_u512(&(upper_signif2 << sh), &luv);
208 if g <= 1 {
211 rem_zero = true;
214 if accept_bounds {
215 lower_rem_zero = is_non_integer;
218 } else {
219 signif10.decr();
222 }
223 } else if g <= 238 {
224 rem_zero = signif2.trailing_zeros() >= g as u32;
227 }
228 }
229
230 let mut i = 0_i32;
233 let mut round_digit = 0_u64;
234 if lower_rem_zero || rem_zero {
235 let (mut lower_quot, mut lower_rem) =
236 lower_signif10.div_rem(10_u64);
237 let (mut upper_quot, mut upper_rem) =
238 upper_signif10.div_rem(10_u64);
239 while lower_quot < upper_quot {
240 (signif10, round_digit) = signif10.div_rem(10_u64);
241 rem_zero &= round_digit == 0;
242 lower_signif10 = lower_quot;
243 lower_rem_zero &= lower_rem == 0;
244 upper_signif10 = upper_quot;
245 i += 1;
246 (lower_quot, lower_rem) = lower_signif10.div_rem(10_u64);
247 (upper_quot, upper_rem) = upper_signif10.div_rem(10_u64);
248 }
249 if lower_rem_zero {
250 while lower_rem == 0 && !lower_signif10.is_zero() {
251 (signif10, round_digit) = signif10.div_rem(10_u64);
252 rem_zero &= round_digit == 0;
253 lower_signif10 = lower_quot;
254 (lower_quot, lower_rem) = lower_signif10.div_rem(10_u64);
255 i += 1;
256 }
257 }
258 if round_digit > 5 || (round_digit == 5 && (!rem_zero || signif10.lo.is_odd()))
261 {
262 signif10.incr();
263 }
264 } else {
265 let mut round_up = false;
267 let (mut lower_quot, mut lower_rem) =
269 lower_signif10.div_rem(100_u64);
270 let (mut upper_quot, mut upper_rem) =
271 upper_signif10.div_rem(100_u64);
272 if upper_quot > lower_quot {
273 let (quot, rem) = signif10.div_rem(100_u64);
274 round_up = rem >= 50;
275 signif10 = quot;
276 lower_signif10 = lower_quot;
277 upper_signif10 = upper_quot;
278 i += 2;
279 }
280 (lower_quot, lower_rem) = lower_signif10.div_rem(10_u64);
281 (upper_quot, upper_rem) = upper_signif10.div_rem(10_u64);
282 while upper_quot > lower_quot {
283 (signif10, round_digit) = signif10.div_rem(10_u64);
284 round_up = round_digit >= 5;
285 lower_signif10 = lower_quot;
286 upper_signif10 = upper_quot;
287 (lower_quot, lower_rem) = lower_signif10.div_rem(10_u64);
288 (upper_quot, upper_rem) = upper_signif10.div_rem(10_u64);
289 i += 1;
290 }
291 if round_up || signif10 == lower_signif10 {
292 signif10.incr();
293 }
294 }
295 exp10 += i;
297
298 (signif10, exp10)
299 }
300
301 #[allow(unsafe_code, trivial_casts)]
302 #[allow(clippy::cast_possible_truncation)]
303 #[allow(clippy::cast_possible_wrap)]
304 pub(crate) fn fmt_scientific(
305 self,
306 exp_mark: char,
307 form: &mut fmt::Formatter<'_>,
308 ) -> fmt::Result {
309 let mut parts: [MaybeUninit<Part<'_>>; 5] =
310 unsafe { MaybeUninit::uninit().assume_init() };
313 let mut n_parts = 0_usize;
314 let zero_padding: &mut Part;
315 let signif = self.signif10.to_string();
316 let digits = signif.as_str();
317 let n_digits = digits.len();
318 let exp10 = self.exp10 + n_digits as i32 - 1;
319 let exp = exp10.to_string();
320 parts[n_parts] = MaybeUninit::new(Part::Digits(&digits[..1]));
321 n_parts += 1;
322 if n_digits > 1 {
323 parts[n_parts] = MaybeUninit::new(Part::Char('.'));
324 n_parts += 1;
325 parts[n_parts] = MaybeUninit::new(Part::Digits(&digits[1..]));
326 n_parts += 1;
327 }
328 parts[n_parts] = MaybeUninit::new(Part::Char(exp_mark));
329 n_parts += 1;
330 parts[n_parts] = MaybeUninit::new(Part::Digits(&exp));
331 n_parts += 1;
332 let formatted = Formatted {
333 parts: unsafe {
335 &*(&parts[..n_parts] as *const [MaybeUninit<Part<'_>>]
336 as *const [Part<'_>])
337 },
338 };
339 formatted.pad_parts(self.sign == 1, form)
340 }
341}
342
343impl fmt::Display for DecNumRepr {
344 #[allow(unsafe_code, trivial_casts)]
345 #[allow(clippy::cast_sign_loss)]
346 #[allow(clippy::cast_possible_truncation)]
347 #[allow(clippy::cast_possible_wrap)]
348 fn fmt(&self, form: &mut fmt::Formatter<'_>) -> fmt::Result {
349 let mut parts: [MaybeUninit<Part<'_>>; 4] =
350 unsafe { MaybeUninit::uninit().assume_init() };
353 let mut n_parts = 0_usize;
354 let zero_padding: &mut Part;
355 let signif = self.signif10.to_string();
356 let digits = signif.as_str();
357 let n_digits = digits.len();
358 let n_int_digits = max(0, (n_digits as i32 + self.exp10)) as usize;
359 let n_frac_digits = max(0, -self.exp10) as usize;
360 if n_int_digits > n_digits {
362 parts[n_parts] = MaybeUninit::new(Part::Digits(digits));
365 n_parts += 1;
366 parts[n_parts] =
367 MaybeUninit::new(Part::Zeroes(n_int_digits - n_digits));
368 n_parts += 1;
369 } else if n_int_digits > 0 {
370 parts[n_parts] =
371 MaybeUninit::new(Part::Digits(&digits[..n_int_digits]));
372 n_parts += 1;
373 } else {
374 parts[n_parts] = MaybeUninit::new(Part::Char('0'));
375 n_parts += 1;
376 }
377 if n_frac_digits > 0 {
379 parts[n_parts] = MaybeUninit::new(Part::Char('.'));
380 n_parts += 1;
381 if n_frac_digits > n_digits {
382 parts[n_parts] =
385 MaybeUninit::new(Part::Zeroes(n_frac_digits - n_digits));
386 n_parts += 1;
387 parts[n_parts] = MaybeUninit::new(Part::Digits(digits));
388 n_parts += 1;
389 } else {
390 parts[n_parts] =
391 MaybeUninit::new(Part::Digits(&digits[n_int_digits..]));
392 n_parts += 1;
393 }
394 }
395 let formatted = Formatted {
396 parts: unsafe {
398 &*(&parts[..n_parts] as *const [MaybeUninit<Part<'_>>]
399 as *const [Part<'_>])
400 },
401 };
402 formatted.pad_parts(self.sign == 1, form)
403 }
404}
405
406#[cfg(test)]
407#[allow(clippy::decimal_literal_representation)]
408mod tests {
409 use alloc::borrow::ToOwned;
410 use core::str::FromStr;
411
412 use super::*;
413
414 #[test]
415 fn test_one() {
416 let f = f256::ONE;
417 let r = DecNumRepr::shortest_from_f256(&f);
418 assert_eq!(
419 r,
420 DecNumRepr {
421 sign: 0,
422 exp10: 0,
423 signif10: U256::new(0, 1)
424 }
425 );
426 }
427
428 #[test]
429 fn test_ten_pow_72() {
430 let f = f256::from_str("10.0e72").unwrap();
431 let r = DecNumRepr::shortest_from_f256(&f);
432 assert_eq!(
433 r,
434 DecNumRepr {
435 sign: 0,
436 exp10: 73,
437 signif10: U256::new(0, 1)
438 }
439 );
440 }
441
442 #[test]
443 fn test_2_pow_251() {
444 let i = U256::new(1_u128 << 123, 0);
445 let f = f256::encode(0, 0, i);
446 let r = DecNumRepr::shortest_from_f256(&f);
447 assert_eq!(
448 r,
449 DecNumRepr {
450 sign: 0,
451 exp10: 5,
452 signif10: U256::new(
453 106338239662793269832304564822427,
454 192627042266604845397347097774975349141
455 )
456 }
457 );
458 }
459
460 #[test]
461 fn test_7e28() {
462 let f = f256::from_str("7e28").unwrap();
463 let r = DecNumRepr::shortest_from_f256(&f);
464 assert_eq!(
465 r,
466 DecNumRepr {
467 sign: 0,
468 exp10: 28,
469 signif10: U256::new(0, 7)
470 }
471 );
472 }
473
474 #[test]
475 fn test_one_half() {
476 let f = f256::encode(0, -1, U256::new(0, 1));
477 let r = DecNumRepr::shortest_from_f256(&f);
478 assert_eq!(
479 r,
480 DecNumRepr {
481 sign: 0,
482 exp10: -1,
483 signif10: U256::new(0, 5)
484 }
485 );
486 }
487
488 #[test]
489 fn test_one_sixteenth() {
490 let f = f256::encode(0, -4, U256::new(0, 1));
491 let r = DecNumRepr::shortest_from_f256(&f);
492 assert_eq!(
493 r,
494 DecNumRepr {
495 sign: 0,
496 exp10: -4,
497 signif10: U256::new(0, 625)
498 }
499 );
500 }
501
502 #[test]
503 fn test_2_pow_237_minus_1() {
504 let i = U256::new(
505 649037107316853453566312041152511,
506 340282366920938463463374607431768211455,
507 );
508 let f = f256::encode(0, 0, i);
509 let r = DecNumRepr::shortest_from_f256(&f);
510 assert_eq!(
511 r,
512 DecNumRepr {
513 sign: 0,
514 exp10: 0,
515 signif10: i,
516 }
517 );
518 }
519
520 #[test]
521 fn test_f256_max() {
522 let f = f256::MAX;
523 let r = DecNumRepr::shortest_from_f256(&f);
524 assert_eq!(
525 r,
526 DecNumRepr {
527 sign: 0,
528 exp10: 78842,
529 signif10: U256::new(
530 473526069559795162737608364600986,
531 168794288209602616731974382256735511567
532 )
533 }
534 );
535 }
536}