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.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 let is_zero = signif.is_zero();
106 let exp = if is_zero { 0 } else { exp };
107
108 let mut signif_str = String::new();
110 write!(&mut signif_str, "{}", signif.in_radix(B as _))?;
111 let signif_str = signif_str.strip_prefix('-').unwrap_or(&signif_str);
115
116 let (left_pad, right_pad) = if let Some(min_width) = f.width() {
118 let mut signif_digits = signif_str.len();
119 let leading_zeros = -(exp + signif_str.len() as isize - 1).min(0) as usize;
121 let mut trailing_zeros = exp.max(0) as usize;
123
124 if let Some(prec) = f.precision() {
126 let diff = prec as isize + exp.min(0);
127 if diff > 0 {
128 trailing_zeros += diff as usize;
129 }
130 }
131 if leading_zeros == 0 {
132 signif_digits = signif_digits.max(1);
134 }
135
136 let has_sign = ((negative && !is_zero) || f.sign_plus()) as usize;
137 let has_radix_point = if exp > 0 {
138 f.precision().unwrap_or(0) > 0
141 } else {
142 f.precision() != Some(0) } as usize;
146
147 let width = signif_digits + has_sign + has_radix_point + leading_zeros + trailing_zeros;
148
149 if width >= min_width {
151 (0, 0)
152 } else if f.sign_aware_zero_pad() {
153 (min_width - width, 0)
154 } else {
155 match f.align() {
156 Some(Alignment::Left) => (0, min_width - width),
157 Some(Alignment::Right) | None => (min_width - width, 0),
158 Some(Alignment::Center) => {
159 let diff = min_width - width;
160 (diff / 2, diff - diff / 2)
161 }
162 }
163 }
164 } else {
165 (0, 0)
166 };
167
168 if !f.sign_aware_zero_pad() {
170 for _ in 0..left_pad {
171 f.write_char(f.fill())?;
172 }
173 }
174 if negative && (!is_zero || f.sign_plus()) {
178 f.write_char('-')?;
179 } else if f.sign_plus() {
180 f.write_char('+')?;
181 }
182 if f.sign_aware_zero_pad() {
183 for _ in 0..left_pad {
184 f.write_char('0')?;
185 }
186 }
187
188 if exp < 0 {
190 let exp = -exp as usize;
192 let (int, fract) = signif_str.split_at(signif_str.len().saturating_sub(exp));
193
194 let frac_digits = fract.len();
195 debug_assert!(frac_digits <= exp);
196
197 if int.is_empty() {
199 f.write_char('0')?;
200 } else {
201 f.write_str(int)?;
202 }
203
204 if let Some(prec) = f.precision() {
206 if prec != 0 {
208 f.write_char('.')?;
209 if exp >= prec {
210 debug_assert!(exp == prec);
212
213 if prec > frac_digits {
215 for _ in 0..prec - frac_digits {
216 f.write_char('0')?;
217 }
218 }
219 if frac_digits > 0 {
220 f.write_str(fract)?;
221 }
222 } else {
223 for _ in 0..exp - frac_digits {
225 f.write_char('0')?;
226 }
227 f.write_str(fract)?;
228 for _ in 0..prec - exp {
229 f.write_char('0')?;
230 }
231 }
232 }
233 } else if frac_digits > 0 {
234 f.write_char('.')?;
235 for _ in 0..(exp - frac_digits) {
236 f.write_char('0')?;
237 }
238 f.write_str(fract)?;
239 }
240 } else {
241 if signif_str.is_empty() {
246 f.write_char('0')?;
248 } else {
249 f.write_str(signif_str)?;
250 }
251 for _ in 0..exp {
252 f.write_char('0')?;
253 }
254
255 if let Some(prec) = f.precision() {
257 if prec > 0 {
258 f.write_char('.')?;
259 for _ in 0..prec {
260 f.write_char('0')?;
261 }
262 }
263 }
264 };
265
266 for _ in 0..right_pad {
268 f.write_char(f.fill())?;
269 }
270
271 Ok(())
272 }
273
274 fn fmt_round_scientific<R: Round>(
280 &self,
281 f: &mut Formatter<'_>,
282 upper: bool,
283 use_hexadecimal: bool,
284 exp_marker: Option<char>,
285 ) -> fmt::Result {
286 assert!(!(B != 2 && use_hexadecimal), "hexadecimal is only relevant for base 2");
287
288 if let Some(result) = self.write_if_infinite(f) {
289 return result;
290 }
291
292 let negative = self.sign() == Sign::Negative;
294 let rounded_signif;
295 let (signif, exp) = if let Some(prec) = f.precision() {
296 let prec = if use_hexadecimal {
298 (prec * 4 + 4) as isize
299 } else {
300 (prec + 1) as isize
301 };
302 let diff = prec - self.digits() as isize;
303 if diff < 0 {
304 let shift = -diff as usize;
305 let (signif, rem) = split_digits_ref::<B>(&self.significand, shift);
306 let adjust = R::round_fract::<B>(&signif, rem, shift);
307 rounded_signif = signif + adjust;
308 (&rounded_signif, self.exponent - diff)
309 } else {
310 (&self.significand, self.exponent)
311 }
312 } else {
313 (&self.significand, self.exponent)
314 };
315 let is_zero = signif.is_zero();
318 let exp = if is_zero { 0 } else { exp };
319
320 let (mut signif_str, mut exp_str) = (String::new(), String::new());
322 match (upper, use_hexadecimal) {
323 (false, false) => write!(&mut signif_str, "{}", signif.in_radix(B as _)),
324 (true, false) => write!(&mut signif_str, "{:#}", signif.in_radix(B as _)),
325 (false, true) => write!(&mut signif_str, "{:}", signif.in_radix(16)),
326 (true, true) => write!(&mut signif_str, "{:#}", signif.in_radix(16)),
327 }?;
328 let signif_str = signif_str.strip_prefix('-').unwrap_or(&signif_str);
332 let exp_adjust = if use_hexadecimal {
334 exp + (signif_str.len() as isize - 1) * 4
335 } else {
336 exp + signif_str.len() as isize - 1
337 };
338 write!(&mut exp_str, "{}", exp_adjust)?;
339 let exp_str = exp_str.as_str();
340
341 let (left_pad, right_pad) = if let Some(min_width) = f.width() {
343 let prec = f.precision().unwrap_or(0);
344 let has_point = signif_str.len() > 1 || prec > 0; let has_sign = (negative && !is_zero) || f.sign_plus();
346
347 let trailing_zeros = if prec > signif_str.len() - 1 {
349 prec - (signif_str.len() - 1)
350 } else {
351 0
352 };
353
354 let width = signif_str.len() + exp_str.len()
355 + 1
356 + has_sign as usize
357 + has_point as usize
358 + use_hexadecimal as usize * 2
359 + trailing_zeros;
360
361 if width >= min_width {
362 (0, 0)
363 } else {
364 match f.align() {
365 Some(Alignment::Left) => (0, min_width - width),
366 Some(Alignment::Right) | None => (min_width - width, 0),
367 Some(Alignment::Center) => {
368 let diff = min_width - width;
369 (diff / 2, diff - diff / 2)
370 }
371 }
372 }
373 } else {
374 (0, 0)
375 };
376
377 if !f.sign_aware_zero_pad() {
379 for _ in 0..left_pad {
380 f.write_char(f.fill())?;
381 }
382 }
383 if negative && (!is_zero || f.sign_plus()) {
387 f.write_char('-')?;
388 } else if f.sign_plus() {
389 f.write_char('+')?;
390 }
391 if use_hexadecimal {
392 f.write_str("0x")?;
393 }
394 if f.sign_aware_zero_pad() {
395 for _ in 0..left_pad {
396 f.write_char('0')?;
397 }
398 }
399
400 let (int, fract) = signif_str.split_at(1);
402 f.write_str(int)?;
403 if !fract.is_empty() {
404 f.write_char('.')?;
405 f.write_str(fract)?;
406 }
407 let prec = f.precision().unwrap_or(0);
408 if prec > 0 {
409 if fract.is_empty() {
410 f.write_char('.')?
411 }
412 for _ in fract.len()..prec {
413 f.write_char('0')?;
414 }
415 }
416
417 f.write_char(exp_marker.unwrap_or('@'))?;
418 f.write_str(exp_str)?;
419
420 for _ in 0..right_pad {
422 f.write_char(f.fill())?;
423 }
424
425 Ok(())
426 }
427}
428
429impl<const B: Word> Display for Repr<B> {
430 #[inline]
431 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
432 self.fmt_round::<Zero>(f)
433 }
434}
435
436impl<R: Round, const B: Word> fmt::Debug for FBig<R, B> {
437 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438 if self.repr.is_infinite() {
440 return match self.repr.sign() {
441 Sign::Positive => f.write_str("inf"),
442 Sign::Negative => f.write_str("-inf"),
443 };
444 }
445
446 let rnd_name = core::any::type_name::<R>();
447 let rnd_name = rnd_name
448 .rfind("::")
449 .map(|pos| &rnd_name[pos + 2..])
450 .unwrap_or(rnd_name);
451
452 if f.alternate() {
453 f.debug_struct("FBig")
454 .field_significand::<B>(&self.repr.significand)
455 .field("exponent", &format_args!("{} ^ {}", B, self.repr.exponent))
456 .field("precision", &self.context.precision)
457 .field("rounding", &format_args!("{}", rnd_name))
458 .finish()
459 } else {
460 f.write_fmt(format_args!("{:?} (prec: {})", self.repr, self.context.precision))
461 }
462 }
463}
464
465impl<R: Round, const B: Word> Display for FBig<R, B> {
466 #[inline]
467 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
468 self.repr.fmt_round::<R>(f)
469 }
470}
471
472macro_rules! impl_fmt_with_base {
473 ($base:literal, $trait:ident, $upper: literal, $hex:literal, $marker:literal) => {
474 impl fmt::$trait for Repr<$base> {
475 #[inline]
476 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
477 self.fmt_round_scientific::<Zero>(f, $upper, $hex, Some($marker))
478 }
479 }
480
481 impl<R: Round> fmt::$trait for FBig<R, $base> {
482 #[inline]
483 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
484 self.repr
485 .fmt_round_scientific::<R>(f, $upper, $hex, Some($marker))
486 }
487 }
488 };
489}
490
491impl_fmt_with_base!(2, LowerHex, false, true, 'p');
495impl_fmt_with_base!(2, UpperHex, true, true, 'p');
496impl_fmt_with_base!(2, Binary, false, false, 'b');
497impl_fmt_with_base!(8, Octal, false, false, 'o');
498impl_fmt_with_base!(16, LowerHex, false, false, 'h');
499impl_fmt_with_base!(16, UpperHex, true, false, 'h');
500
501impl<const B: Word> fmt::LowerExp for Repr<B> {
502 #[inline]
503 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
504 let marker = match B {
505 10 => Some('e'),
506 _ => None,
507 };
508 self.fmt_round_scientific::<Zero>(f, false, false, marker)
509 }
510}
511impl<const B: Word> fmt::UpperExp for Repr<B> {
512 #[inline]
513 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
514 let marker = match B {
515 10 => Some('E'),
516 _ => None,
517 };
518 self.fmt_round_scientific::<Zero>(f, true, false, marker)
519 }
520}
521impl<R: Round, const B: Word> fmt::LowerExp for FBig<R, B> {
522 #[inline]
523 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
524 let marker = match B {
525 10 => Some('e'),
526 _ => None,
527 };
528 self.repr.fmt_round_scientific::<R>(f, false, false, marker)
529 }
530}
531impl<R: Round, const B: Word> fmt::UpperExp for FBig<R, B> {
532 #[inline]
533 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
534 let marker = match B {
535 10 => Some('E'),
536 _ => None,
537 };
538 self.repr.fmt_round_scientific::<R>(f, true, false, marker)
539 }
540}