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 let Some(result) = self.write_if_infinite(f) {
38 return result;
39 }
40
41 if f.alternate() {
42 f.debug_struct("Repr")
43 .field_significand::<B>(&self.significand)
44 .field("exponent", &format_args!("{} ^ {}", &B, &self.exponent))
45 .finish()
46 } else {
47 f.write_fmt(format_args!("{:?} * {} ^ {}", &self.significand, &B, &self.exponent))
48 }
49 }
50}
51
52impl<R: Round> fmt::Debug for Context<R> {
53 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
54 let rnd_name = core::any::type_name::<R>();
55 let rnd_name = rnd_name
56 .rfind("::")
57 .map(|pos| &rnd_name[pos + 2..])
58 .unwrap_or(rnd_name);
59 f.debug_struct("Context")
60 .field("precision", &self.precision)
61 .field("rounding", &format_args!("{}", rnd_name))
62 .finish()
63 }
64}
65
66impl<const B: Word> Repr<B> {
67 fn write_if_infinite(&self, f: &mut Formatter<'_>) -> Option<fmt::Result> {
69 if self.is_infinite() {
70 Some(match self.sign() {
71 Sign::Positive => f.write_str("inf"),
72 Sign::Negative => f.write_str("-inf"),
73 })
74 } else {
75 None
76 }
77 }
78
79 fn fmt_round<R: Round>(&self, f: &mut Formatter<'_>) -> fmt::Result {
82 if let Some(result) = self.write_if_infinite(f) {
83 return result;
84 }
85
86 let negative = self.significand.sign() == Sign::Negative;
88 let rounded_signif;
89 let (signif, exp) = if let Some(prec) = f.precision() {
90 let diff = prec as isize + self.exponent;
91 if diff < 0 {
92 let shift = -diff as usize;
93 let (signif, rem) = split_digits_ref::<B>(&self.significand, shift);
94 let adjust = R::round_fract::<B>(&signif, rem, shift);
95 rounded_signif = signif + adjust;
96 (&rounded_signif, self.exponent - diff)
97 } else {
98 (&self.significand, self.exponent)
99 }
100 } else {
101 (&self.significand, self.exponent)
102 };
103
104 let mut signif_str = String::new();
106 write!(&mut signif_str, "{}", signif.in_radix(B as _))?;
107 let signif_str = if negative {
108 &signif_str[1..]
109 } else {
110 signif_str.as_str()
111 };
112
113 let (left_pad, right_pad) = if let Some(min_width) = f.width() {
115 let mut signif_digits = signif_str.len();
116 let leading_zeros = -(exp + signif_str.len() as isize - 1).min(0) as usize;
118 let mut trailing_zeros = exp.max(0) as usize;
120
121 if let Some(prec) = f.precision() {
123 let diff = prec as isize + exp.min(0);
124 if diff > 0 {
125 trailing_zeros += diff as usize;
126 }
127 }
128 if leading_zeros == 0 {
129 signif_digits = signif_digits.max(1);
131 }
132
133 let has_sign = (negative || f.sign_plus()) as usize;
134 let has_radix_point = if exp > 0 {
135 f.precision().unwrap_or(0) > 0
138 } else {
139 f.precision() != Some(0) } as usize;
143
144 let width = signif_digits + has_sign + has_radix_point + leading_zeros + trailing_zeros;
145
146 if width >= min_width {
148 (0, 0)
149 } else if f.sign_aware_zero_pad() {
150 (min_width - width, 0)
151 } else {
152 match f.align() {
153 Some(Alignment::Left) => (0, min_width - width),
154 Some(Alignment::Right) | None => (min_width - width, 0),
155 Some(Alignment::Center) => {
156 let diff = min_width - width;
157 (diff / 2, diff - diff / 2)
158 }
159 }
160 }
161 } else {
162 (0, 0)
163 };
164
165 if !f.sign_aware_zero_pad() {
167 for _ in 0..left_pad {
168 f.write_char(f.fill())?;
169 }
170 }
171 if negative {
172 f.write_char('-')?;
173 } else if f.sign_plus() {
174 f.write_char('+')?;
175 }
176 if f.sign_aware_zero_pad() {
177 for _ in 0..left_pad {
178 f.write_char('0')?;
179 }
180 }
181
182 if exp < 0 {
184 let exp = -exp as usize;
186 let (int, fract) = signif_str.split_at(signif_str.len().saturating_sub(exp));
187
188 let frac_digits = fract.len();
189 debug_assert!(frac_digits <= exp);
190
191 if int.is_empty() {
193 f.write_char('0')?;
194 } else {
195 f.write_str(int)?;
196 }
197
198 if let Some(prec) = f.precision() {
200 if prec != 0 {
202 f.write_char('.')?;
203 if exp >= prec {
204 debug_assert!(exp == prec);
206
207 if prec > frac_digits {
209 for _ in 0..prec - frac_digits {
210 f.write_char('0')?;
211 }
212 }
213 if frac_digits > 0 {
214 f.write_str(fract)?;
215 }
216 } else {
217 for _ in 0..exp - frac_digits {
219 f.write_char('0')?;
220 }
221 f.write_str(fract)?;
222 for _ in 0..prec - exp {
223 f.write_char('0')?;
224 }
225 }
226 }
227 } else if frac_digits > 0 {
228 f.write_char('.')?;
229 for _ in 0..(exp - frac_digits) {
230 f.write_char('0')?;
231 }
232 f.write_str(fract)?;
233 }
234 } else {
235 if signif_str.is_empty() {
240 f.write_char('0')?;
242 } else {
243 f.write_str(signif_str)?;
244 }
245 for _ in 0..exp {
246 f.write_char('0')?;
247 }
248
249 if let Some(prec) = f.precision() {
251 if prec > 0 {
252 f.write_char('.')?;
253 for _ in 0..prec {
254 f.write_char('0')?;
255 }
256 }
257 }
258 };
259
260 for _ in 0..right_pad {
262 f.write_char(f.fill())?;
263 }
264
265 Ok(())
266 }
267
268 fn fmt_round_scientific<R: Round>(
274 &self,
275 f: &mut Formatter<'_>,
276 upper: bool,
277 use_hexadecimal: bool,
278 exp_marker: Option<char>,
279 ) -> fmt::Result {
280 assert!(!(B != 2 && use_hexadecimal), "hexadecimal is only relevant for base 2");
281
282 if let Some(result) = self.write_if_infinite(f) {
283 return result;
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}