1use crate::{
4 fbig::FBig,
5 repr::{Context, Repr},
6 round::{mode::Zero, Round},
7 utils::{digit_len, split_digits_ref},
8};
9use alloc::string::String;
10use core::fmt::{self, Alignment, Display, Formatter, Write};
11use dashu_base::Sign;
12use dashu_int::{IBig, Word};
13
14trait DebugStructHelper {
15 fn field_significand<const B: Word>(&mut self, signif: &IBig) -> &mut Self;
17}
18
19impl<'a, 'b> DebugStructHelper for fmt::DebugStruct<'a, 'b> {
20 fn field_significand<const B: Word>(&mut self, signif: &IBig) -> &mut Self {
21 match B {
22 2 => self.field(
23 "significand",
24 &format_args!("{:?} ({} bits)", signif, digit_len::<B>(signif)),
25 ),
26 10 => self.field("significand", &format_args!("{:#?}", signif)),
27 _ => self.field(
28 "significand",
29 &format_args!("{:?} ({} digits)", signif, digit_len::<B>(signif)),
30 ),
31 }
32 }
33}
34
35impl<const B: Word> fmt::Debug for Repr<B> {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 if self.is_infinite() {
39 return match self.sign() {
40 Sign::Positive => f.write_str("inf"),
41 Sign::Negative => f.write_str("-inf"),
42 };
43 }
44
45 if f.alternate() {
46 f.debug_struct("Repr")
47 .field_significand::<B>(&self.significand)
48 .field("exponent", &format_args!("{} ^ {}", &B, &self.exponent))
49 .finish()
50 } else {
51 f.write_fmt(format_args!("{:?} * {} ^ {}", &self.significand, &B, &self.exponent))
52 }
53 }
54}
55
56impl<R: Round> fmt::Debug for Context<R> {
57 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
58 let rnd_name = core::any::type_name::<R>();
59 let rnd_name = rnd_name
60 .rfind("::")
61 .map(|pos| &rnd_name[pos + 2..])
62 .unwrap_or(rnd_name);
63 f.debug_struct("Context")
64 .field("precision", &self.precision)
65 .field("rounding", &format_args!("{}", rnd_name))
66 .finish()
67 }
68}
69
70impl<const B: Word> Repr<B> {
71 fn fmt_round<R: Round>(&self, f: &mut Formatter<'_>) -> fmt::Result {
74 if self.is_infinite() {
76 return match self.sign() {
77 Sign::Positive => f.write_str("inf"),
78 Sign::Negative => f.write_str("-inf"),
79 };
80 }
81
82 let negative = self.significand.sign() == Sign::Negative;
84 let rounded_signif;
85 let (signif, exp) = if let Some(prec) = f.precision() {
86 let diff = prec as isize + self.exponent;
87 if diff < 0 {
88 let shift = -diff as usize;
89 let (signif, rem) = split_digits_ref::<B>(&self.significand, shift);
90 let adjust = R::round_fract::<B>(&signif, rem, shift);
91 rounded_signif = signif + adjust;
92 (&rounded_signif, self.exponent - diff)
93 } else {
94 (&self.significand, self.exponent)
95 }
96 } else {
97 (&self.significand, self.exponent)
98 };
99
100 let mut signif_str = String::new();
102 write!(&mut signif_str, "{}", signif.in_radix(B as _))?;
103 let signif_str = if negative {
104 &signif_str[1..]
105 } else {
106 signif_str.as_str()
107 };
108
109 let (left_pad, right_pad) = if let Some(min_width) = f.width() {
111 let mut signif_digits = signif_str.len();
112 let leading_zeros = -(exp + signif_str.len() as isize - 1).min(0) as usize;
114 let mut trailing_zeros = exp.max(0) as usize;
116
117 if let Some(prec) = f.precision() {
119 let diff = prec as isize + exp.min(0);
120 if diff > 0 {
121 trailing_zeros += diff as usize;
122 }
123 }
124 if leading_zeros == 0 {
125 signif_digits = signif_digits.max(1);
127 }
128
129 let has_sign = (negative || f.sign_plus()) as usize;
130 let has_radix_point = if exp > 0 {
131 f.precision().unwrap_or(0) > 0
134 } else {
135 f.precision() != Some(0) } as usize;
139
140 let width = signif_digits + has_sign + has_radix_point + leading_zeros + trailing_zeros;
141
142 if width >= min_width {
144 (0, 0)
145 } else if f.sign_aware_zero_pad() {
146 (min_width - width, 0)
147 } else {
148 match f.align() {
149 Some(Alignment::Left) => (0, min_width - width),
150 Some(Alignment::Right) | None => (min_width - width, 0),
151 Some(Alignment::Center) => {
152 let diff = min_width - width;
153 (diff / 2, diff - diff / 2)
154 }
155 }
156 }
157 } else {
158 (0, 0)
159 };
160
161 if !f.sign_aware_zero_pad() {
163 for _ in 0..left_pad {
164 f.write_char(f.fill())?;
165 }
166 }
167 if negative {
168 f.write_char('-')?;
169 } else if f.sign_plus() {
170 f.write_char('+')?;
171 }
172 if f.sign_aware_zero_pad() {
173 for _ in 0..left_pad {
174 f.write_char('0')?;
175 }
176 }
177
178 if exp < 0 {
180 let exp = -exp as usize;
182 let (int, fract) = signif_str.split_at(signif_str.len().saturating_sub(exp));
183
184 let frac_digits = fract.len();
185 debug_assert!(frac_digits <= exp);
186
187 if int.is_empty() {
189 f.write_char('0')?;
190 } else {
191 f.write_str(int)?;
192 }
193
194 if let Some(prec) = f.precision() {
196 if prec != 0 {
198 f.write_char('.')?;
199 if exp >= prec {
200 debug_assert!(exp == prec);
202
203 if prec > frac_digits {
205 for _ in 0..prec - frac_digits {
206 f.write_char('0')?;
207 }
208 }
209 if frac_digits > 0 {
210 f.write_str(fract)?;
211 }
212 } else {
213 for _ in 0..exp - frac_digits {
215 f.write_char('0')?;
216 }
217 f.write_str(fract)?;
218 for _ in 0..prec - exp {
219 f.write_char('0')?;
220 }
221 }
222 }
223 } else if frac_digits > 0 {
224 f.write_char('.')?;
225 for _ in 0..(exp - frac_digits) {
226 f.write_char('0')?;
227 }
228 f.write_str(fract)?;
229 }
230 } else {
231 if signif_str.is_empty() {
236 f.write_char('0')?;
238 } else {
239 f.write_str(signif_str)?;
240 }
241 for _ in 0..exp {
242 f.write_char('0')?;
243 }
244
245 if let Some(prec) = f.precision() {
247 if prec > 0 {
248 f.write_char('.')?;
249 for _ in 0..prec {
250 f.write_char('0')?;
251 }
252 }
253 }
254 };
255
256 for _ in 0..right_pad {
258 f.write_char(f.fill())?;
259 }
260
261 Ok(())
262 }
263
264 fn fmt_round_scientific<R: Round>(
270 &self,
271 f: &mut Formatter<'_>,
272 upper: bool,
273 use_hexadecimal: bool,
274 exp_marker: Option<char>,
275 ) -> fmt::Result {
276 assert!(!(B != 2 && use_hexadecimal), "hexadecimal is only relevant for base 2");
277
278 if self.is_infinite() {
280 return match self.sign() {
281 Sign::Positive => f.write_str("inf"),
282 Sign::Negative => f.write_str("-inf"),
283 };
284 }
285
286 let negative = self.significand.sign() == Sign::Negative;
288 let rounded_signif;
289 let (signif, exp) = if let Some(prec) = f.precision() {
290 let prec = if use_hexadecimal {
292 (prec * 4 + 4) as isize
293 } else {
294 (prec + 1) as isize
295 };
296 let diff = prec - self.digits() as isize;
297 if diff < 0 {
298 let shift = -diff as usize;
299 let (signif, rem) = split_digits_ref::<B>(&self.significand, shift);
300 let adjust = R::round_fract::<B>(&signif, rem, shift);
301 rounded_signif = signif + adjust;
302 (&rounded_signif, self.exponent - diff)
303 } else {
304 (&self.significand, self.exponent)
305 }
306 } else {
307 (&self.significand, self.exponent)
308 };
309
310 let (mut signif_str, mut exp_str) = (String::new(), String::new());
312 match (upper, use_hexadecimal) {
313 (false, false) => write!(&mut signif_str, "{}", signif.in_radix(B as _)),
314 (true, false) => write!(&mut signif_str, "{:#}", signif.in_radix(B as _)),
315 (false, true) => write!(&mut signif_str, "{:}", signif.in_radix(16)),
316 (true, true) => write!(&mut signif_str, "{:#}", signif.in_radix(16)),
317 }?;
318 let signif_str = if negative {
319 &signif_str[1..]
320 } else {
321 signif_str.as_str()
322 };
323 let exp_adjust = if use_hexadecimal {
325 exp + (signif_str.len() as isize - 1) * 4
326 } else {
327 exp + signif_str.len() as isize - 1
328 };
329 write!(&mut exp_str, "{}", exp_adjust)?;
330 let exp_str = exp_str.as_str();
331
332 let (left_pad, right_pad) = if let Some(min_width) = f.width() {
334 let prec = f.precision().unwrap_or(0);
335 let has_point = signif_str.len() > 1 || prec > 0; let has_sign = negative || f.sign_plus();
337
338 let trailing_zeros = if prec > signif_str.len() - 1 {
340 prec - (signif_str.len() - 1)
341 } else {
342 0
343 };
344
345 let width = signif_str.len() + exp_str.len()
346 + 1
347 + has_sign as usize
348 + has_point as usize
349 + use_hexadecimal as usize * 2
350 + trailing_zeros;
351
352 if width >= min_width {
353 (0, 0)
354 } else {
355 match f.align() {
356 Some(Alignment::Left) => (0, min_width - width),
357 Some(Alignment::Right) | None => (min_width - width, 0),
358 Some(Alignment::Center) => {
359 let diff = min_width - width;
360 (diff / 2, diff - diff / 2)
361 }
362 }
363 }
364 } else {
365 (0, 0)
366 };
367
368 if !f.sign_aware_zero_pad() {
370 for _ in 0..left_pad {
371 f.write_char(f.fill())?;
372 }
373 }
374 if negative {
375 f.write_char('-')?;
376 } else if f.sign_plus() {
377 f.write_char('+')?;
378 }
379 if use_hexadecimal {
380 f.write_str("0x")?;
381 }
382 if f.sign_aware_zero_pad() {
383 for _ in 0..left_pad {
384 f.write_char('0')?;
385 }
386 }
387
388 let (int, fract) = signif_str.split_at(1);
390 f.write_str(int)?;
391 if !fract.is_empty() {
392 f.write_char('.')?;
393 f.write_str(fract)?;
394 }
395 let prec = f.precision().unwrap_or(0);
396 if prec > 0 {
397 if fract.is_empty() {
398 f.write_char('.')?
399 }
400 for _ in fract.len()..prec {
401 f.write_char('0')?;
402 }
403 }
404
405 f.write_char(exp_marker.unwrap_or('@'))?;
406 f.write_str(exp_str)?;
407
408 for _ in 0..right_pad {
410 f.write_char(f.fill())?;
411 }
412
413 Ok(())
414 }
415}
416
417impl<const B: Word> Display for Repr<B> {
418 #[inline]
419 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
420 self.fmt_round::<Zero>(f)
421 }
422}
423
424impl<R: Round, const B: Word> fmt::Debug for FBig<R, B> {
425 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426 if self.repr.is_infinite() {
428 return match self.repr.sign() {
429 Sign::Positive => f.write_str("inf"),
430 Sign::Negative => f.write_str("-inf"),
431 };
432 }
433
434 let rnd_name = core::any::type_name::<R>();
435 let rnd_name = rnd_name
436 .rfind("::")
437 .map(|pos| &rnd_name[pos + 2..])
438 .unwrap_or(rnd_name);
439
440 if f.alternate() {
441 f.debug_struct("FBig")
442 .field_significand::<B>(&self.repr.significand)
443 .field("exponent", &format_args!("{} ^ {}", &B, &self.repr.exponent))
444 .field("precision", &self.context.precision)
445 .field("rounding", &format_args!("{}", rnd_name))
446 .finish()
447 } else {
448 f.write_fmt(format_args!("{:?} (prec: {})", &self.repr, &self.context.precision))
449 }
450 }
451}
452
453impl<R: Round, const B: Word> Display for FBig<R, B> {
454 #[inline]
455 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
456 self.repr.fmt_round::<R>(f)
457 }
458}
459
460macro_rules! impl_fmt_with_base {
461 ($base:literal, $trait:ident, $upper: literal, $hex:literal, $marker:literal) => {
462 impl fmt::$trait for Repr<$base> {
463 #[inline]
464 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
465 self.fmt_round_scientific::<Zero>(f, $upper, $hex, Some($marker))
466 }
467 }
468
469 impl<R: Round> fmt::$trait for FBig<R, $base> {
470 #[inline]
471 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
472 self.repr
473 .fmt_round_scientific::<R>(f, $upper, $hex, Some($marker))
474 }
475 }
476 };
477}
478
479impl_fmt_with_base!(2, LowerHex, false, true, 'p');
483impl_fmt_with_base!(2, UpperHex, true, true, 'p');
484impl_fmt_with_base!(2, Binary, false, false, 'b');
485impl_fmt_with_base!(8, Octal, false, false, 'o');
486impl_fmt_with_base!(16, LowerHex, false, false, 'h');
487impl_fmt_with_base!(16, UpperHex, true, false, 'h');
488
489impl<const B: Word> fmt::LowerExp for Repr<B> {
490 #[inline]
491 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
492 let marker = match B {
493 10 => Some('e'),
494 _ => None,
495 };
496 self.fmt_round_scientific::<Zero>(f, false, false, marker)
497 }
498}
499impl<const B: Word> fmt::UpperExp for Repr<B> {
500 #[inline]
501 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
502 let marker = match B {
503 10 => Some('E'),
504 _ => None,
505 };
506 self.fmt_round_scientific::<Zero>(f, true, false, marker)
507 }
508}
509impl<R: Round, const B: Word> fmt::LowerExp for FBig<R, B> {
510 #[inline]
511 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
512 let marker = match B {
513 10 => Some('e'),
514 _ => None,
515 };
516 self.repr.fmt_round_scientific::<R>(f, false, false, marker)
517 }
518}
519impl<R: Round, const B: Word> fmt::UpperExp for FBig<R, B> {
520 #[inline]
521 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
522 let marker = match B {
523 10 => Some('E'),
524 _ => None,
525 };
526 self.repr.fmt_round_scientific::<R>(f, true, false, marker)
527 }
528}