1use crate::{
17 helpers::IntHelper,
18 types::extra::{False, LeEqU128, LeEqU16, LeEqU32, LeEqU64, LeEqU8},
19 FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64,
20 FixedU8,
21};
22use core::{
23 cmp::{self, Ordering},
24 fmt::{
25 Alignment, Binary, Debug, Display, Formatter, LowerHex, Octal, Result as FmtResult,
26 UpperHex,
27 },
28 mem, str,
29};
30
31struct Buffer {
44 int_digits: usize,
45 frac_digits: usize,
46 data: [u8; 130],
47}
48
49impl Buffer {
50 fn new() -> Buffer {
51 Buffer {
52 int_digits: 0,
53 frac_digits: 0,
54 data: [0; 130],
55 }
56 }
57
58 fn set_len(&mut self, int_digits: u32, frac_digits: u32) {
61 assert!(int_digits + frac_digits < 130, "out of bounds");
62 self.int_digits = int_digits as usize;
63 self.frac_digits = frac_digits as usize;
64 self.data[1 + self.int_digits] = b'.';
65 }
66
67 fn int(&mut self) -> &mut [u8] {
69 let begin = 1;
70 let end = begin + self.int_digits;
71 &mut self.data[begin..end]
72 }
73
74 fn frac(&mut self) -> &mut [u8] {
75 let begin = 1 + self.int_digits + 1;
76 let end = begin + self.frac_digits;
77 &mut self.data[begin..end]
78 }
79
80 fn finish(
81 &mut self,
82 radix: Radix,
83 is_neg: bool,
84 frac_rem_cmp_msb: Ordering,
85 fmt: &mut Formatter,
86 ) -> FmtResult {
87 self.round_and_trim(radix.max(), frac_rem_cmp_msb);
88 self.encode_digits(radix == Radix::UpHex);
89 self.pad_and_print(is_neg, radix.prefix(), fmt)
90 }
91
92 fn round_and_trim(&mut self, max: u8, frac_rem_cmp_msb: Ordering) {
93 let len = if self.frac_digits > 0 {
94 self.int_digits + self.frac_digits + 2
95 } else {
96 self.int_digits + 1
97 };
98
99 let round_up = frac_rem_cmp_msb == Ordering::Greater
100 || frac_rem_cmp_msb == Ordering::Equal && self.data[len - 1].is_odd();
101 if round_up {
102 for b in self.data[0..len].iter_mut().rev() {
103 if *b < max {
104 *b += 1;
105 break;
106 }
107 if *b == b'.' {
108 debug_assert!(self.frac_digits == 0);
109 continue;
110 }
111 *b = 0;
112 if self.frac_digits > 0 {
113 self.frac_digits -= 1;
114 }
115 }
116 } else {
117 let mut trim = 0;
118 for b in self.frac().iter().rev() {
119 if *b != 0 {
120 break;
121 }
122 trim += 1;
123 }
124 self.frac_digits -= trim;
125 }
126 }
127
128 fn encode_digits(&mut self, upper: bool) {
129 for digit in self.data[..self.int_digits + self.frac_digits + 2].iter_mut() {
130 if *digit < 10 {
131 *digit += b'0';
132 } else if *digit < 16 {
133 *digit += if upper { b'A' - 10 } else { b'a' - 10 };
134 }
135 }
136 }
137
138 fn pad_and_print(&self, is_neg: bool, maybe_prefix: &str, fmt: &mut Formatter) -> FmtResult {
139 use core::fmt::Write;
140
141 let sign = if is_neg {
142 "-"
143 } else if fmt.sign_plus() {
144 "+"
145 } else {
146 ""
147 };
148 let prefix = if fmt.alternate() { maybe_prefix } else { "" };
149
150 let abs_begin = if self.data[0] != b'0' || self.data[1] == b'.' {
164 0
165 } else if self.data[1] == b'0' {
166 2
167 } else {
168 1
169 };
170 let end_zeros = fmt.precision().map(|x| x - self.frac_digits).unwrap_or(0);
171 let abs_end = if self.frac_digits > 0 {
172 self.int_digits + self.frac_digits + 2
173 } else if end_zeros > 0 {
174 self.int_digits + 2
175 } else {
176 self.int_digits + 1
177 };
178
179 let req_width = sign.len() + prefix.len() + abs_end - abs_begin + end_zeros;
180 let pad = fmt
181 .width()
182 .and_then(|w| w.checked_sub(req_width))
183 .unwrap_or(0);
184 let (pad_left, pad_zeros, pad_right) = if fmt.sign_aware_zero_pad() {
185 (0, pad, 0)
186 } else {
187 match fmt.align() {
188 Some(Alignment::Left) => (0, 0, pad),
189 Some(Alignment::Center) => (pad / 2, 0, pad - pad / 2),
190 None | Some(Alignment::Right) => (pad, 0, 0),
191 }
192 };
193 let fill = fmt.fill();
194
195 for _ in 0..pad_left {
196 fmt.write_char(fill)?;
197 }
198 fmt.write_str(sign)?;
199 fmt.write_str(prefix)?;
200 for _ in 0..pad_zeros {
201 fmt.write_char('0')?;
202 }
203 fmt.write_str(str::from_utf8(&self.data[abs_begin..abs_end]).unwrap())?;
204 for _ in 0..end_zeros {
205 fmt.write_char('0')?;
206 }
207 for _ in 0..pad_right {
208 fmt.write_char(fill)?;
209 }
210 Ok(())
211 }
212}
213
214#[derive(Clone, Copy, Eq, PartialEq, scale_info::TypeInfo)]
215enum Radix {
216 Bin,
217 Oct,
218 LowHex,
219 UpHex,
220 Dec,
221}
222impl Radix {
223 fn digit_bits(self) -> u32 {
224 match self {
225 Radix::Bin => 1,
226 Radix::Oct => 3,
227 Radix::LowHex => 4,
228 Radix::UpHex => 4,
229 Radix::Dec => 4,
230 }
231 }
232 fn max(self) -> u8 {
233 match self {
234 Radix::Bin => 1,
235 Radix::Oct => 7,
236 Radix::LowHex => 15,
237 Radix::UpHex => 15,
238 Radix::Dec => 9,
239 }
240 }
241 fn prefix(self) -> &'static str {
242 match self {
243 Radix::Bin => "0b",
244 Radix::Oct => "0o",
245 Radix::LowHex => "0x",
246 Radix::UpHex => "0x",
247 Radix::Dec => "",
248 }
249 }
250}
251
252trait FmtHelper: IntHelper<IsSigned = False> {
253 fn write_int(self, radix: Radix, nbits: u32, buf: &mut Buffer);
254 fn write_frac(self, radix: Radix, nbits: u32, buf: &mut Buffer) -> Ordering;
255 fn write_int_dec(self, nbits: u32, buf: &mut Buffer);
256 fn write_frac_dec(self, nbits: u32, auto_prec: bool, buf: &mut Buffer) -> Ordering;
257}
258
259macro_rules! impl_radix_helper {
260 ($U:ident, $H:ident, $attempt_half:expr) => {
261 impl FmtHelper for $U {
262 fn write_int(mut self, radix: Radix, nbits: u32, buf: &mut Buffer) {
263 if $attempt_half && nbits < $U::NBITS / 2 {
264 return (self as $H).write_int(radix, nbits, buf);
265 }
266 let digit_bits = radix.digit_bits();
267 let mask = radix.max();
268 for b in buf.int().iter_mut().rev() {
269 debug_assert!(self != 0);
270 *b = self.lower_byte() & mask;
271 self >>= digit_bits;
272 }
273 debug_assert!(self == 0);
274 }
275 fn write_frac(mut self, radix: Radix, nbits: u32, buf: &mut Buffer) -> Ordering {
276 if $attempt_half && nbits < $U::NBITS / 2 {
277 return ((self >> ($U::NBITS / 2)) as $H).write_frac(radix, nbits, buf);
278 }
279 let digit_bits = radix.digit_bits();
280 let compl_digit_bits = $U::NBITS - digit_bits;
281 for b in buf.frac().iter_mut() {
282 debug_assert!(self != 0);
283 *b = (self >> compl_digit_bits).lower_byte();
284 self <<= digit_bits;
285 }
286 self.cmp(&$U::MSB)
287 }
288 fn write_int_dec(mut self, nbits: u32, buf: &mut Buffer) {
289 if $attempt_half && nbits < $U::NBITS / 2 {
290 return (self as $H).write_int_dec(nbits, buf);
291 }
292 for b in buf.int().iter_mut().rev() {
293 *b = (self % 10).lower_byte();
294 self /= 10;
295 }
296 debug_assert!(self == 0);
297 }
298 fn write_frac_dec(mut self, nbits: u32, auto_prec: bool, buf: &mut Buffer) -> Ordering {
299 if $attempt_half && nbits < $U::NBITS / 2 {
300 return ((self >> ($U::NBITS / 2)) as $H).write_frac_dec(nbits, auto_prec, buf);
301 }
302
303 let (mut tie, mut add_5) = if nbits == $U::NBITS {
305 (0, true)
306 } else {
307 ($U::MSB >> nbits, false)
308 };
309 let mut trim_to = None;
310 for (i, b) in buf.frac().iter_mut().enumerate() {
311 *b = self.mul10_assign();
312
313 if self < 10 || self.wrapping_neg() < 10 {
316 trim_to = Some(i + 1);
317 break;
318 }
319
320 if auto_prec {
321 tie.mul10_assign();
324 if add_5 {
325 tie += 5;
326 add_5 = false;
327 }
328 if self < tie || self.wrapping_neg() < tie {
329 trim_to = Some(i + 1);
330 break;
331 }
332 }
333 }
334 if let Some(trim_to) = trim_to {
335 buf.frac_digits = trim_to;
336 }
337 self.cmp(&$U::MSB)
338 }
339 }
340 };
341}
342
343impl_radix_helper! { u8, u8, false }
344impl_radix_helper! { u16, u8, true }
345impl_radix_helper! { u32, u16, true }
346impl_radix_helper! { u64, u32, true }
347impl_radix_helper! { u128, u64, true }
348
349fn fmt_dec<U: FmtHelper>((neg, abs): (bool, U), frac_nbits: u32, fmt: &mut Formatter) -> FmtResult {
350 let (int, frac) = if frac_nbits == 0 {
351 (abs, U::ZERO)
352 } else if frac_nbits == U::NBITS {
353 (U::ZERO, abs)
354 } else {
355 (abs >> frac_nbits, abs << (U::NBITS - frac_nbits))
356 };
357 let int_used_nbits = U::NBITS - int.leading_zeros();
358 let int_digits = ceil_log10_2_times(int_used_nbits);
359 let frac_used_nbits = U::NBITS - frac.trailing_zeros();
360 let (frac_digits, auto_prec) = if let Some(precision) = fmt.precision() {
361 (cmp::min(frac_used_nbits as usize, precision) as u32, false)
363 } else {
364 (ceil_log10_2_times(frac_nbits), true)
365 };
366
367 let mut buf = Buffer::new();
368 buf.set_len(int_digits, frac_digits);
369 int.write_int_dec(int_used_nbits, &mut buf);
370 let frac_rem_cmp_msb = frac.write_frac_dec(frac_nbits, auto_prec, &mut buf);
371 buf.finish(Radix::Dec, neg, frac_rem_cmp_msb, fmt)
372}
373
374fn fmt_radix2<U: FmtHelper>(
375 (neg, abs): (bool, U),
376 frac_nbits: u32,
377 radix: Radix,
378 fmt: &mut Formatter,
379) -> FmtResult {
380 let (int, frac) = if frac_nbits == 0 {
381 (abs, U::ZERO)
382 } else if frac_nbits == U::NBITS {
383 (U::ZERO, abs)
384 } else {
385 (abs >> frac_nbits, abs << (U::NBITS - frac_nbits))
386 };
387 let digit_bits = radix.digit_bits();
388 let int_used_nbits = U::NBITS - int.leading_zeros();
389 let int_digits = (int_used_nbits + digit_bits - 1) / digit_bits;
390 let frac_used_nbits = U::NBITS - frac.trailing_zeros();
391 let mut frac_digits = (frac_used_nbits + digit_bits - 1) / digit_bits;
392 if let Some(precision) = fmt.precision() {
393 frac_digits = cmp::min(frac_digits as usize, precision) as u32;
395 }
396
397 let mut buf = Buffer::new();
398 buf.set_len(int_digits, frac_digits);
399 int.write_int(radix, int_used_nbits, &mut buf);
400 let frac_rem_cmp_msb = frac.write_frac(radix, frac_used_nbits, &mut buf);
402 buf.finish(radix, neg, frac_rem_cmp_msb, fmt)
403}
404
405macro_rules! impl_fmt {
406 ($Fixed:ident($LeEqU:ident)) => {
407 impl<Frac: $LeEqU> Display for $Fixed<Frac> {
408 fn fmt(&self, f: &mut Formatter) -> FmtResult {
409 fmt_dec(self.to_bits().neg_abs(), Self::FRAC_NBITS, f)
410 }
411 }
412
413 impl<Frac: $LeEqU> Debug for $Fixed<Frac> {
414 fn fmt(&self, f: &mut Formatter) -> FmtResult {
415 fmt_dec(self.to_bits().neg_abs(), Self::FRAC_NBITS, f)
416 }
417 }
418
419 impl<Frac: $LeEqU> Binary for $Fixed<Frac> {
420 fn fmt(&self, f: &mut Formatter) -> FmtResult {
421 fmt_radix2(self.to_bits().neg_abs(), Self::FRAC_NBITS, Radix::Bin, f)
422 }
423 }
424
425 impl<Frac: $LeEqU> Octal for $Fixed<Frac> {
426 fn fmt(&self, f: &mut Formatter) -> FmtResult {
427 fmt_radix2(self.to_bits().neg_abs(), Self::FRAC_NBITS, Radix::Oct, f)
428 }
429 }
430
431 impl<Frac: $LeEqU> LowerHex for $Fixed<Frac> {
432 fn fmt(&self, f: &mut Formatter) -> FmtResult {
433 fmt_radix2(self.to_bits().neg_abs(), Self::FRAC_NBITS, Radix::LowHex, f)
434 }
435 }
436
437 impl<Frac: $LeEqU> UpperHex for $Fixed<Frac> {
438 fn fmt(&self, f: &mut Formatter) -> FmtResult {
439 fmt_radix2(self.to_bits().neg_abs(), Self::FRAC_NBITS, Radix::UpHex, f)
440 }
441 }
442 };
443}
444
445impl_fmt! { FixedU8(LeEqU8) }
446impl_fmt! { FixedU16(LeEqU16) }
447impl_fmt! { FixedU32(LeEqU32) }
448impl_fmt! { FixedU64(LeEqU64) }
449impl_fmt! { FixedU128(LeEqU128) }
450impl_fmt! { FixedI8(LeEqU8) }
451impl_fmt! { FixedI16(LeEqU16) }
452impl_fmt! { FixedI32(LeEqU32) }
453impl_fmt! { FixedI64(LeEqU64) }
454impl_fmt! { FixedI128(LeEqU128) }
455
456fn ceil_log10_2_times(int_bits: u32) -> u32 {
458 debug_assert!(int_bits < 112_816);
459 ((u64::from(int_bits) * 0x4D10_4D43 + 0xFFFF_FFFF) >> 32) as u32
460}
461
462pub(crate) trait Mul10: Sized {
463 fn mul10_assign(&mut self) -> u8;
464}
465macro_rules! mul10_widen {
466 ($Single:ty, $Double:ty) => {
467 impl Mul10 for $Single {
468 #[inline]
469 fn mul10_assign(&mut self) -> u8 {
470 const NBITS: usize = 8 * mem::size_of::<$Single>();
471 let prod = <$Double>::from(*self) * 10;
472 *self = prod as $Single;
473 (prod >> NBITS) as u8
474 }
475 }
476 };
477}
478mul10_widen! { u8, u16 }
479mul10_widen! { u16, u32 }
480mul10_widen! { u32, u64 }
481mul10_widen! { u64, u128 }
482impl Mul10 for u128 {
483 #[inline]
484 fn mul10_assign(&mut self) -> u8 {
485 const LO_MASK: u128 = !(!0 << 64);
486 let hi = (*self >> 64) * 10;
487 let lo = (*self & LO_MASK) * 10;
488 let (hi_lo, hi_hi) = (hi as u64, (hi >> 64) as u64);
492 let (lo_lo, lo_hi) = (lo as u64, (lo >> 64) as u64);
493 let (wrapped, overflow) = hi_lo.overflowing_add(lo_hi);
494 *self = (u128::from(wrapped) << 64) | u128::from(lo_lo);
495 hi_hi as u8 + u8::from(overflow)
496 }
497}
498
499#[cfg(test)]
500#[allow(clippy::cognitive_complexity, clippy::float_cmp)]
501mod tests {
502 use crate::{display, types::*};
503 use std::{
504 format,
505 string::{String, ToString},
506 };
507
508 #[test]
509 fn format() {
510 let pos = I16F16::from_num(12.3);
511 assert_eq!(format!("{:+}", pos), "+12.3");
512 assert_eq!(format!("{:+08}", pos), "+00012.3");
513 assert_eq!(format!("{:+#08}", pos), "+00012.3");
514 assert_eq!(format!("{:+08X}", pos), "+0C.4CCD");
515 assert_eq!(format!("{:+08.1X}", pos), "+0000C.5");
516 assert_eq!(format!("{:+#08X}", pos), "+0xC.4CCD");
517 assert_eq!(format!("{:+#08.1X}", pos), "+0x00C.5");
518
519 assert_eq!(format!("{:#<8}", pos), "12.3####");
520 assert_eq!(format!("{:#^8}", pos), "##12.3##");
521 assert_eq!(format!("{:#^9}", pos), "##12.3###");
522 assert_eq!(format!("{:#>8}", pos), "####12.3");
523 assert_eq!(format!("{:#^08}", pos), "000012.3");
524 }
525
526 fn trim_frac_zeros(mut x: &str) -> &str {
527 while x.ends_with('0') {
528 x = &x[..x.len() - 1];
529 }
530 if x.ends_with('.') {
531 x = &x[..x.len() - 1];
532 }
533 x
534 }
535
536 fn up_frac_digits(x: &mut String, frac_digits: usize) {
537 if let Some(point) = x.find('.') {
538 if let Some(additional) = frac_digits.checked_sub(x.len() - point - 1) {
539 x.reserve(additional);
540 for _ in 0..additional {
541 x.push('0');
542 }
543 }
544 } else {
545 x.reserve(frac_digits + 1);
546 x.push('.');
547 for _ in 0..frac_digits {
548 x.push('0');
549 }
550 }
551 }
552
553 #[test]
554 fn hex() {
555 for i in 0..(1u32 << 7) {
556 let p = 0x1234_5678_9abc_def0u64 ^ u64::from(i);
557 let n = -0x1234_5678_9abc_def0i64 ^ i64::from(i);
558 let f_p = U57F7::from_bits(p);
559 let f_n = I57F7::from_bits(n);
560 let mut check_p = format!("{:x}.{:02x}", p >> 7, (p & 0x7f) << 1);
561 up_frac_digits(&mut check_p, 1000);
562 let trimmed_p = trim_frac_zeros(&check_p);
563 let mut check_n = format!("-{:x}.{:02x}", n.abs() >> 7, (n.abs() & 0x7f) << 1);
564 up_frac_digits(&mut check_n, 1000);
565 let trimmed_n = trim_frac_zeros(&check_n);
566 assert_eq!(format!("{:.1000x}", f_p), check_p);
567 assert_eq!(format!("{:x}", f_p), trimmed_p);
568 assert_eq!(format!("{:.1000x}", f_n), check_n);
569 assert_eq!(format!("{:x}", f_n), trimmed_n);
570 }
571 }
572
573 #[test]
574 fn dec() {
575 for i in 0..(1 << 7) {
576 let bits = (!0u32 >> 8) ^ i;
578 let fix = U25F7::from_bits(bits);
579 let flt = (bits as f32) / 7f32.exp2();
580 assert_eq!(format!("{}", fix), format!("{}", flt));
581 assert_eq!(U25F7::from_num(flt), fix);
582 assert_eq!(fix.to_num::<f32>(), flt);
583 }
584 }
585
586 #[test]
587 fn display_frac() {
588 assert_eq!(
589 format!("{:X}", I0F128::from_bits(!0)),
590 "-0.00000000000000000000000000000001"
591 );
592 assert_eq!(format!("{:X}", I0F64::from_bits(!0)), "-0.0000000000000001");
593 assert_eq!(format!("{:X}", I0F32::from_bits(!0)), "-0.00000001");
594 assert_eq!(format!("{:X}", I0F16::from_bits(!0)), "-0.0001");
595 assert_eq!(format!("{:X}", I0F8::from_bits(!0)), "-0.01");
596 assert_eq!(
597 format!("{:X}", U0F128::from_bits(!0)),
598 "0.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
599 );
600 assert_eq!(format!("{:X}", U0F64::from_bits(!0)), "0.FFFFFFFFFFFFFFFF");
601 assert_eq!(format!("{:X}", U0F32::from_bits(!0)), "0.FFFFFFFF");
602 assert_eq!(format!("{:X}", U0F16::from_bits(!0)), "0.FFFF");
603 assert_eq!(format!("{:X}", U0F8::from_bits(!0)), "0.FF");
604
605 assert_eq!(
606 format!("{}", I0F128::from_bits(!0)),
607 "-0.000000000000000000000000000000000000003"
608 );
609 assert_eq!(
610 format!("{}", I0F64::from_bits(!0)),
611 "-0.00000000000000000005"
612 );
613 assert_eq!(format!("{}", I0F32::from_bits(!0)), "-0.0000000002");
614 assert_eq!(format!("{}", I0F16::from_bits(!0)), "-0.00002");
615 assert_eq!(format!("{}", I0F8::from_bits(!0)), "-0.004");
616 assert_eq!(
617 format!("{}", U0F128::from_bits(!0)),
618 "0.999999999999999999999999999999999999997"
619 );
620 assert_eq!(
621 format!("{}", U0F64::from_bits(!0)),
622 "0.99999999999999999995"
623 );
624 assert_eq!(format!("{}", U0F32::from_bits(!0)), "0.9999999998");
625 assert_eq!(format!("{}", U0F16::from_bits(!0)), "0.99998");
626 assert_eq!(format!("{}", U0F8::from_bits(!0)), "0.996");
627
628 let no_internal_overflow_bits = 0xe666_6666_6666_6665_ffff_ffff_ffff_ffffu128;
630 let internal_overflow_bits = 0xe666_6666_6666_6666_ffff_ffff_ffff_ffffu128;
631 assert_eq!(
632 format!("{:X}", U0F128::from_bits(no_internal_overflow_bits)),
633 "0.E666666666666665FFFFFFFFFFFFFFFF"
634 );
635 assert_eq!(
636 format!("{:X}", U0F128::from_bits(internal_overflow_bits)),
637 "0.E666666666666666FFFFFFFFFFFFFFFF"
638 );
639 assert_eq!(
640 format!("{}", U0F128::from_bits(no_internal_overflow_bits)),
641 "0.899999999999999999978315956550289911317"
642 );
643 assert_eq!(
644 format!("{}", U0F128::from_bits(internal_overflow_bits)),
645 "0.900000000000000000032526065174565133017"
646 );
647 }
648
649 #[test]
650 fn close_to_round_decimal() {
651 for i in 0..1000u16 {
652 let float = f32::from(i + 1000) / 1000.;
655 let fix = U9F23::from_num(float);
656 let check = format!("1.{:03}", i);
657 assert_eq!(format!("{}", fix), trim_frac_zeros(&check));
658 assert_eq!(format!("{}", fix), format!("{}", float));
659 for prec in 0..10 {
660 assert_eq!(format!("{:.*}", prec, fix), format!("{:.*}", prec, float));
661 }
662 }
663 }
664
665 #[test]
666 fn check_ceil_log10_2_times() {
667 for i in 0..112_816 {
668 let check = (f64::from(i) * 2f64.log10()).ceil() as u32;
669 assert_eq!(display::ceil_log10_2_times(i), check);
670 }
671 }
672
673 #[test]
674 fn rounding() {
675 let i = U8F8::from_bits(0xFF80);
676 assert_eq!(format!("{}", i), "255.5");
677 assert_eq!(format!("{:.0}", i), "256");
678 assert_eq!(format!("{:b}", i), "11111111.1");
679 assert_eq!(format!("{:.0b}", i), "100000000");
680 assert_eq!(format!("{:o}", i), "377.4");
681 assert_eq!(format!("{:.0o}", i), "400");
682 assert_eq!(format!("{:X}", i), "FF.8");
683 assert_eq!(format!("{:.0X}", i), "100");
684
685 let i = U8F8::from_bits(0xFE80);
686 assert_eq!(format!("{}", i), "254.5");
687 assert_eq!(format!("{:.0}", i), "254");
688 assert_eq!(format!("{:b}", i), "11111110.1");
689 assert_eq!(format!("{:.0b}", i), "11111110");
690 assert_eq!(format!("{:o}", i), "376.4");
691 assert_eq!(format!("{:.0o}", i), "376");
692 assert_eq!(format!("{:X}", i), "FE.8");
693 assert_eq!(format!("{:.0X}", i), "FE");
694
695 let i = U8F8::from_bits(0xDDDD);
696 assert_eq!(format!("{}", i), "221.863");
697 assert_eq!(format!("{:.0}", i), "222");
698 assert_eq!(format!("{:.1}", i), "221.9");
699 assert_eq!(format!("{:.2}", i), "221.86");
700 assert_eq!(format!("{:.3}", i), "221.863");
701 assert_eq!(format!("{:.4}", i), "221.8633");
702 assert_eq!(format!("{:.5}", i), "221.86328");
703 assert_eq!(format!("{:.6}", i), "221.863281");
704 assert_eq!(format!("{:.7}", i), "221.8632812");
705 assert_eq!(format!("{:.8}", i), "221.86328125");
706 assert_eq!(format!("{:.9}", i), "221.863281250");
707 assert_eq!(format!("{:b}", i), "11011101.11011101");
708 assert_eq!(format!("{:.0b}", i), "11011110");
709 assert_eq!(format!("{:.1b}", i), "11011110.0");
710 assert_eq!(format!("{:.2b}", i), "11011101.11");
711 assert_eq!(format!("{:.3b}", i), "11011101.111");
712 assert_eq!(format!("{:.4b}", i), "11011101.1110");
713 assert_eq!(format!("{:.5b}", i), "11011101.11100");
714 assert_eq!(format!("{:.6b}", i), "11011101.110111");
715 assert_eq!(format!("{:.7b}", i), "11011101.1101110");
716 assert_eq!(format!("{:.8b}", i), "11011101.11011101");
717 assert_eq!(format!("{:.9b}", i), "11011101.110111010");
718 assert_eq!(format!("{:o}", i), "335.672");
719 assert_eq!(format!("{:.0o}", i), "336");
720 assert_eq!(format!("{:.1o}", i), "335.7");
721 assert_eq!(format!("{:.2o}", i), "335.67");
722 assert_eq!(format!("{:.3o}", i), "335.672");
723 assert_eq!(format!("{:.4o}", i), "335.6720");
724 assert_eq!(format!("{:X}", i), "DD.DD");
725 assert_eq!(format!("{:.0X}", i), "DE");
726 assert_eq!(format!("{:.0X}", i), "DE");
727 assert_eq!(format!("{:.1X}", i), "DD.E");
728 assert_eq!(format!("{:.2X}", i), "DD.DD");
729 assert_eq!(format!("{:.3X}", i), "DD.DD0");
730 }
731
732 #[test]
733 fn compare_frac0_int() {
734 for u in 0..=255u8 {
735 let i = u as i8;
736 let (ifix, ufix) = (I8F0::from_bits(i), U8F0::from_bits(u));
737 assert_eq!(ifix.to_string(), i.to_string());
738 assert_eq!(ufix.to_string(), u.to_string());
739 if i >= 0 {
740 assert_eq!(format!("{:#X}", ifix), format!("{:#X}", i));
741 assert_eq!(format!("{:#b}", ifix), format!("{:#b}", i));
742 } else {
743 let abs_i = i.wrapping_neg() as u8;
744 assert_eq!(format!("{:#X}", ifix), format!("-{:#X}", abs_i));
745 assert_eq!(format!("{:#b}", ifix), format!("-{:#b}", abs_i));
746 }
747 assert_eq!(format!("{:#x}", ufix), format!("{:#x}", u));
748 assert_eq!(format!("{:#o}", ufix), format!("{:#o}", u));
749 }
750 }
751
752 #[test]
753 fn compare_frac4_float() {
754 for u in 0..=255u8 {
755 let (ifix, ufix) = (I4F4::from_bits(u as i8), U4F4::from_bits(u));
760 let (iflo, uflo) = (ifix.to_num::<f32>(), ufix.to_num::<f32>());
761 let (sifix, sufix) = (ifix.to_string(), ufix.to_string());
762 let pifix = sifix.find('.').map(|p| sifix.len() - 1 - p).unwrap_or(0);
763 let pufix = sufix.find('.').map(|p| sufix.len() - 1 - p).unwrap_or(0);
764 let (siflo, suflo) = (format!("{:.*}", pifix, iflo), format!("{:.*}", pufix, uflo));
765 assert_eq!(sifix, siflo);
766 assert_eq!(sufix, suflo);
767
768 let ifixed =
773 I28F4::from(ifix) + I28F4::from_num(i32::from(ifix.to_bits().signum()) << 19);
774 let ufixed = U28F4::from(ufix) + U28F4::from_num(1 << 19);
775 let (ifloat, ufloat) = (ifixed.to_num::<f32>(), ufixed.to_num::<f32>());
776 let (sifixed, sufixed) = (ifixed.to_string(), ufixed.to_string());
777 let (sifloat, sufloat) = (ifloat.to_string(), ufloat.to_string());
778 assert_eq!(sifixed, sifloat);
779 assert_eq!(sufixed, sufloat);
780
781 let sifix_frac = sifix.find('.').map(|i| &sifix[i..]);
785 let sifixed_frac = sifixed.find('.').map(|i| &sifixed[i..]);
786 assert_eq!(sifix_frac, sifixed_frac);
787 let sufix_frac = sufix.find('.').map(|i| &sufix[i..]);
788 let sufixed_frac = sufixed.find('.').map(|i| &sufixed[i..]);
789 assert_eq!(sufix_frac, sufixed_frac);
790 }
791 }
792
793 #[test]
794 fn compare_frac17_float() {
795 for u in 0..(1 << 17) {
796 let fix = U15F17::from_bits(u) + U15F17::from_num(99);
798 let fix_pos = I15F17::from_num(fix);
799 let fix_neg = -fix_pos;
800 let (flo, flo_neg) = (fix.to_num::<f32>(), fix_neg.to_num::<f32>());
801
802 let fix_str = fix.to_string();
803 let fix_pos_str = fix_pos.to_string();
804 let fix_neg_str = fix_neg.to_string();
805 assert_eq!(fix_str, flo.to_string());
806 assert_eq!(fix_str, fix_pos_str);
807 assert_eq!(fix_neg_str, flo_neg.to_string());
808 if u != 0 {
809 assert_eq!(&fix_neg_str[..1], "-");
810 assert_eq!(&fix_neg_str[1..], fix_pos_str);
811 }
812
813 let fix_str3 = format!("{:.3}", fix);
814 let fix_pos_str3 = format!("{:.3}", fix_pos);
815 let fix_neg_str3 = format!("{:.3}", fix_neg);
816 assert_eq!(fix_str3, format!("{:.3}", flo));
817 assert_eq!(fix_str3, fix_pos_str3);
818 assert_eq!(fix_neg_str3, format!("{:.3}", flo_neg));
819 if u != 0 {
820 assert_eq!(&fix_neg_str3[..1], "-");
821 assert_eq!(&fix_neg_str3[1..], fix_pos_str3);
822 }
823 }
824 }
825}