From f8023dbe7ce3e0ca66460b8d9499f7befda8fe9b Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Mon, 22 Jul 2019 12:05:15 -0700 Subject: [PATCH 01/26] WIP: add more formatting traits TODO: eliminate heap use, write a macro for Display impls --- src/lib.rs | 159 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 148 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3aac82e..864c9b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,7 @@ use num_traits::{ }; mod pow; +use fmt::{Binary, Display, Formatter, LowerHex, Octal, UpperHex}; /// Represents the ratio between two numbers. #[derive(Copy, Clone, Debug)] @@ -1000,16 +1001,136 @@ impl Signed for Ratio { } // String conversions -impl fmt::Display for Ratio +impl Display for Ratio where - T: fmt::Display + Eq + One, + T: Display + Clone + Integer, { /// Renders as `numer/denom`. If denom=1, renders as numer. - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.denom.is_one() { - write!(f, "{}", self.numer) + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + //write!(f,"{}", self.denom); + let non_negative = !(self < & as Zero>::zero()); + let tmp = if self.denom.is_one() { + alloc::format!("{}", self.numer) + } else { + alloc::format!("{}/{}", self.numer, self.denom) + }; + if non_negative { + f.pad_integral(non_negative, "", &tmp) + } else { + f.pad_integral(non_negative, "", &tmp[1..tmp.len()]) + } + } + // fn fmt(&self, f: &mut Formatter) -> fmt::Result { + // let non_negative = !(self < & as Zero>::zero()); + // let tmp = if self.denom.is_one() { + // std::format!("{}", self.numer) + // } else { + // std::format!("{}/{}", self.numer, self.denom) + // }; + // if non_negative { + // f.pad_integral(non_negative, "", &tmp) + // } else { + // f.pad_integral(non_negative, "", &tmp[1..tmp.len()]) + // } + // } +} + +impl Octal for Ratio +where + T: Octal + Clone + Integer, +{ + /// Renders as `numer/denom`. If denom=1, renders as numer. + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let non_negative = !(self < & as Zero>::zero()); + let prefix = "0o"; + let tmp = if self.denom.is_one() { + alloc::format!("{:o}", self.numer) + } else { + if f.alternate() { + alloc::format!("{:o}/{:#o}", self.numer, self.denom) + } else { + alloc::format!("{:o}/{:o}", self.numer, self.denom) + } + }; + if non_negative { + f.pad_integral(non_negative, prefix, &tmp) + } else { + f.pad_integral(non_negative, prefix, &tmp[1..tmp.len()]) + } + } +} + +impl Binary for Ratio +where + T: Binary + Clone + Integer, +{ + /// Renders as `numer/denom`. If denom=1, renders as numer. + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let non_negative = !(self < & as Zero>::zero()); + let prefix = "0b"; + let tmp = if self.denom.is_one() { + alloc::format!("{:b}", self.numer) + } else { + if f.alternate() { + alloc::format!("{:b}/{:#b}", self.numer, self.denom) + } else { + alloc::format!("{:b}/{:b}", self.numer, self.denom) + } + }; + if non_negative { + f.pad_integral(non_negative, prefix, &tmp) + } else { + f.pad_integral(non_negative, prefix, &tmp[1..tmp.len()]) + } + } +} + +impl LowerHex for Ratio +where + T: LowerHex + Clone + Integer, +{ + /// Renders as `numer/denom`. If denom=1, renders as numer. + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let non_negative = !(self < & as Zero>::zero()); + let prefix = "0x"; + let tmp = if self.denom.is_one() { + alloc::format!("{:x}", self.numer) + } else { + if f.alternate() { + alloc::format!("{:x}/{:#x}", self.numer, self.denom) + } else { + alloc::format!("{:x}/{:x}", self.numer, self.denom) + } + }; + if non_negative { + f.pad_integral(non_negative, prefix, &tmp) + } else { + f.pad_integral(non_negative, prefix, &tmp[1..tmp.len()]) + } + } +} + +impl UpperHex for Ratio +where + T: UpperHex + Clone + Integer, +{ + /// Renders as `numer/denom`. If denom=1, renders as numer. + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let non_negative = !(self < & as Zero>::zero()); + let prefix = "0x"; + let tmp = if self.denom.is_one() { + alloc::format!("{:X}", self.numer) + } else { + if f.alternate() { + alloc::format!("{:X}/{:#X}", self.numer, self.denom) + } else { + alloc::format!("{:X}/{:X}", self.numer, self.denom) + } + }; + if non_negative { + f.pad_integral(non_negative, prefix, &tmp) } else { - write!(f, "{}/{}", self.numer, self.denom) + f.pad_integral(non_negative, prefix, &tmp[1..tmp.len()]) } } } @@ -1560,11 +1681,27 @@ mod test { #[test] #[cfg(feature = "std")] fn test_show() { - use std::string::ToString; - assert_eq!(format!("{}", _2), "2".to_string()); - assert_eq!(format!("{}", _1_2), "1/2".to_string()); - assert_eq!(format!("{}", _0), "0".to_string()); - assert_eq!(format!("{}", Ratio::from_integer(-2)), "-2".to_string()); + // Test: + // :b :o :x, :X, :? + // alternate or not (#) + // positive and negative + // padding + // does not test precision (i.e. truncation) + assert_eq!(&format!("{}", _2), "2"); + assert_eq!(&format!("{}", _1_2), "1/2"); + assert_eq!(&format!("{:7}", _1_2), " 1/2"); //test padding + assert_eq!(&format!("{}", -_1_2), "-1/2"); // test negatives + assert_eq!(&format!("{:7}", -_1_2), " -1/2"); //test padding and negatives + assert_eq!(&format!("{:07}", -_1_2), "-0001/2"); + assert_eq!(&format!("{}", _0), "0"); + assert_eq!(&format!("{}", Ratio::from_integer(-2)), "-2"); + assert_eq!(&format!("{:b}", _2), "10"); + assert_eq!(&format!("{:b}", _1_2), "1/10"); + assert_eq!(&format!("{:b}", _0), "0"); + assert_eq!(&format!("{:b}", Ratio::from_integer(2)), "10"); + assert_eq!(&format!("{:#b}", _1_2), "0b1/0b10"); + assert_eq!(&format!("{:010b}", _1_2), "0000001/10"); + assert_eq!(&format!("{:#010b}", _1_2), "0b001/0b10"); } mod arith { From b1a95e7aa0391628c0e87d41e455b2de9914ff1b Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Mon, 29 Jul 2019 03:21:38 -0700 Subject: [PATCH 02/26] make negative check more sane --- src/lib.rs | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 864c9b7..b2652ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1007,8 +1007,7 @@ where { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { - //write!(f,"{}", self.denom); - let non_negative = !(self < & as Zero>::zero()); + let non_negative = *self >= Self::zero(); let tmp = if self.denom.is_one() { alloc::format!("{}", self.numer) } else { @@ -1020,19 +1019,6 @@ where f.pad_integral(non_negative, "", &tmp[1..tmp.len()]) } } - // fn fmt(&self, f: &mut Formatter) -> fmt::Result { - // let non_negative = !(self < & as Zero>::zero()); - // let tmp = if self.denom.is_one() { - // std::format!("{}", self.numer) - // } else { - // std::format!("{}/{}", self.numer, self.denom) - // }; - // if non_negative { - // f.pad_integral(non_negative, "", &tmp) - // } else { - // f.pad_integral(non_negative, "", &tmp[1..tmp.len()]) - // } - // } } impl Octal for Ratio @@ -1041,7 +1027,7 @@ where { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let non_negative = !(self < & as Zero>::zero()); + let non_negative = *self >= Self::zero(); let prefix = "0o"; let tmp = if self.denom.is_one() { alloc::format!("{:o}", self.numer) @@ -1066,7 +1052,7 @@ where { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let non_negative = !(self < & as Zero>::zero()); + let non_negative = *self >= Self::zero(); let prefix = "0b"; let tmp = if self.denom.is_one() { alloc::format!("{:b}", self.numer) @@ -1091,7 +1077,7 @@ where { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let non_negative = !(self < & as Zero>::zero()); + let non_negative = *self >= Self::zero(); let prefix = "0x"; let tmp = if self.denom.is_one() { alloc::format!("{:x}", self.numer) @@ -1116,7 +1102,7 @@ where { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let non_negative = !(self < & as Zero>::zero()); + let non_negative = *self >= Self::zero(); let prefix = "0x"; let tmp = if self.denom.is_one() { alloc::format!("{:X}", self.numer) From e96d0ed6ada89128f742294536551e81cd6eaae8 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Mon, 29 Jul 2019 03:26:25 -0700 Subject: [PATCH 03/26] revert to old implementation of Display --- src/lib.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b2652ab..a54c49f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1003,20 +1003,14 @@ impl Signed for Ratio { // String conversions impl Display for Ratio where - T: Display + Clone + Integer, + T: Display + Eq + One, { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let non_negative = *self >= Self::zero(); - let tmp = if self.denom.is_one() { - alloc::format!("{}", self.numer) - } else { - alloc::format!("{}/{}", self.numer, self.denom) - }; - if non_negative { - f.pad_integral(non_negative, "", &tmp) + if self.denom.is_one() { + write!(f, "{}", self.numer) } else { - f.pad_integral(non_negative, "", &tmp[1..tmp.len()]) + write!(f, "{}/{}", self.numer, self.denom) } } } @@ -1675,9 +1669,7 @@ mod test { // does not test precision (i.e. truncation) assert_eq!(&format!("{}", _2), "2"); assert_eq!(&format!("{}", _1_2), "1/2"); - assert_eq!(&format!("{:7}", _1_2), " 1/2"); //test padding assert_eq!(&format!("{}", -_1_2), "-1/2"); // test negatives - assert_eq!(&format!("{:7}", -_1_2), " -1/2"); //test padding and negatives assert_eq!(&format!("{:07}", -_1_2), "-0001/2"); assert_eq!(&format!("{}", _0), "0"); assert_eq!(&format!("{}", Ratio::from_integer(-2)), "-2"); From c1e3ab3a11454fea60534e46dd763c35fa8135fe Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Mon, 29 Jul 2019 03:31:16 -0700 Subject: [PATCH 04/26] switch from alloc::format to std::alloc --- src/lib.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a54c49f..a8d51c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ #![allow(clippy::suspicious_arithmetic_impl)] #![allow(clippy::suspicious_op_assign_impl)] + #[cfg(feature = "std")] #[cfg_attr(test, macro_use)] extern crate std; @@ -1015,6 +1016,7 @@ where } } +#[cfg(feature = "std")] impl Octal for Ratio where T: Octal + Clone + Integer, @@ -1024,12 +1026,12 @@ where let non_negative = *self >= Self::zero(); let prefix = "0o"; let tmp = if self.denom.is_one() { - alloc::format!("{:o}", self.numer) + std::format!("{:o}", self.numer) } else { if f.alternate() { - alloc::format!("{:o}/{:#o}", self.numer, self.denom) + std::format!("{:o}/{:#o}", self.numer, self.denom) } else { - alloc::format!("{:o}/{:o}", self.numer, self.denom) + std::format!("{:o}/{:o}", self.numer, self.denom) } }; if non_negative { @@ -1040,6 +1042,7 @@ where } } +#[cfg(feature = "std")] impl Binary for Ratio where T: Binary + Clone + Integer, @@ -1049,12 +1052,12 @@ where let non_negative = *self >= Self::zero(); let prefix = "0b"; let tmp = if self.denom.is_one() { - alloc::format!("{:b}", self.numer) + std::format!("{:b}", self.numer) } else { if f.alternate() { - alloc::format!("{:b}/{:#b}", self.numer, self.denom) + std::format!("{:b}/{:#b}", self.numer, self.denom) } else { - alloc::format!("{:b}/{:b}", self.numer, self.denom) + std::format!("{:b}/{:b}", self.numer, self.denom) } }; if non_negative { @@ -1065,6 +1068,7 @@ where } } +#[cfg(feature = "std")] impl LowerHex for Ratio where T: LowerHex + Clone + Integer, @@ -1074,12 +1078,12 @@ where let non_negative = *self >= Self::zero(); let prefix = "0x"; let tmp = if self.denom.is_one() { - alloc::format!("{:x}", self.numer) + std::format!("{:x}", self.numer) } else { if f.alternate() { - alloc::format!("{:x}/{:#x}", self.numer, self.denom) + std::format!("{:x}/{:#x}", self.numer, self.denom) } else { - alloc::format!("{:x}/{:x}", self.numer, self.denom) + std::format!("{:x}/{:x}", self.numer, self.denom) } }; if non_negative { @@ -1090,6 +1094,7 @@ where } } +#[cfg(feature = "std")] impl UpperHex for Ratio where T: UpperHex + Clone + Integer, @@ -1099,12 +1104,12 @@ where let non_negative = *self >= Self::zero(); let prefix = "0x"; let tmp = if self.denom.is_one() { - alloc::format!("{:X}", self.numer) + std::format!("{:X}", self.numer) } else { if f.alternate() { - alloc::format!("{:X}/{:#X}", self.numer, self.denom) + std::format!("{:X}/{:#X}", self.numer, self.denom) } else { - alloc::format!("{:X}/{:X}", self.numer, self.denom) + std::format!("{:X}/{:X}", self.numer, self.denom) } }; if non_negative { @@ -1670,7 +1675,6 @@ mod test { assert_eq!(&format!("{}", _2), "2"); assert_eq!(&format!("{}", _1_2), "1/2"); assert_eq!(&format!("{}", -_1_2), "-1/2"); // test negatives - assert_eq!(&format!("{:07}", -_1_2), "-0001/2"); assert_eq!(&format!("{}", _0), "0"); assert_eq!(&format!("{}", Ratio::from_integer(-2)), "-2"); assert_eq!(&format!("{:b}", _2), "10"); From a3c45e31ca125161bec1ac44bedf9e8de10755f1 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Fri, 9 Aug 2019 13:21:56 -0700 Subject: [PATCH 05/26] fix format! macro in 1.26.0 and older rust --- src/lib.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a8d51c7..edd4d82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,7 @@ #[cfg(feature = "std")] -#[cfg_attr(test, macro_use)] +#[macro_use] extern crate std; use core::cmp; @@ -1026,12 +1026,12 @@ where let non_negative = *self >= Self::zero(); let prefix = "0o"; let tmp = if self.denom.is_one() { - std::format!("{:o}", self.numer) + format!("{:o}", self.numer) } else { if f.alternate() { - std::format!("{:o}/{:#o}", self.numer, self.denom) + format!("{:o}/{:#o}", self.numer, self.denom) } else { - std::format!("{:o}/{:o}", self.numer, self.denom) + format!("{:o}/{:o}", self.numer, self.denom) } }; if non_negative { @@ -1052,12 +1052,12 @@ where let non_negative = *self >= Self::zero(); let prefix = "0b"; let tmp = if self.denom.is_one() { - std::format!("{:b}", self.numer) + format!("{:b}", self.numer) } else { if f.alternate() { - std::format!("{:b}/{:#b}", self.numer, self.denom) + format!("{:b}/{:#b}", self.numer, self.denom) } else { - std::format!("{:b}/{:b}", self.numer, self.denom) + format!("{:b}/{:b}", self.numer, self.denom) } }; if non_negative { @@ -1078,12 +1078,12 @@ where let non_negative = *self >= Self::zero(); let prefix = "0x"; let tmp = if self.denom.is_one() { - std::format!("{:x}", self.numer) + format!("{:x}", self.numer) } else { if f.alternate() { - std::format!("{:x}/{:#x}", self.numer, self.denom) + format!("{:x}/{:#x}", self.numer, self.denom) } else { - std::format!("{:x}/{:x}", self.numer, self.denom) + format!("{:x}/{:x}", self.numer, self.denom) } }; if non_negative { @@ -1104,12 +1104,12 @@ where let non_negative = *self >= Self::zero(); let prefix = "0x"; let tmp = if self.denom.is_one() { - std::format!("{:X}", self.numer) + format!("{:X}", self.numer) } else { if f.alternate() { - std::format!("{:X}/{:#X}", self.numer, self.denom) + format!("{:X}/{:#X}", self.numer, self.denom) } else { - std::format!("{:X}/{:X}", self.numer, self.denom) + format!("{:X}/{:X}", self.numer, self.denom) } }; if non_negative { From ebe4b2ef84ff941c0c0185539201d2e479af3a65 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Mon, 12 Aug 2019 17:19:08 -0700 Subject: [PATCH 06/26] remove negative signs from some formatting traits --- src/lib.rs | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index edd4d82..4424a11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1023,9 +1023,8 @@ where { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let non_negative = *self >= Self::zero(); let prefix = "0o"; - let tmp = if self.denom.is_one() { + let pre_pad = if self.denom.is_one() { format!("{:o}", self.numer) } else { if f.alternate() { @@ -1034,11 +1033,7 @@ where format!("{:o}/{:o}", self.numer, self.denom) } }; - if non_negative { - f.pad_integral(non_negative, prefix, &tmp) - } else { - f.pad_integral(non_negative, prefix, &tmp[1..tmp.len()]) - } + f.pad_integral(true, prefix, &pre_pad) } } @@ -1049,9 +1044,8 @@ where { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let non_negative = *self >= Self::zero(); let prefix = "0b"; - let tmp = if self.denom.is_one() { + let pre_pad = if self.denom.is_one() { format!("{:b}", self.numer) } else { if f.alternate() { @@ -1060,11 +1054,9 @@ where format!("{:b}/{:b}", self.numer, self.denom) } }; - if non_negative { - f.pad_integral(non_negative, prefix, &tmp) - } else { - f.pad_integral(non_negative, prefix, &tmp[1..tmp.len()]) - } + // negative numbers are printed as two's compliment + // with no negative sign + f.pad_integral(true, prefix, &pre_pad) } } @@ -1075,9 +1067,8 @@ where { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let non_negative = *self >= Self::zero(); let prefix = "0x"; - let tmp = if self.denom.is_one() { + let pre_pad = if self.denom.is_one() { format!("{:x}", self.numer) } else { if f.alternate() { @@ -1086,11 +1077,7 @@ where format!("{:x}/{:x}", self.numer, self.denom) } }; - if non_negative { - f.pad_integral(non_negative, prefix, &tmp) - } else { - f.pad_integral(non_negative, prefix, &tmp[1..tmp.len()]) - } + f.pad_integral(true, prefix, &pre_pad) } } @@ -1101,9 +1088,8 @@ where { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let non_negative = *self >= Self::zero(); let prefix = "0x"; - let tmp = if self.denom.is_one() { + let pre_pad = if self.denom.is_one() { format!("{:X}", self.numer) } else { if f.alternate() { @@ -1112,11 +1098,7 @@ where format!("{:X}/{:X}", self.numer, self.denom) } }; - if non_negative { - f.pad_integral(non_negative, prefix, &tmp) - } else { - f.pad_integral(non_negative, prefix, &tmp[1..tmp.len()]) - } + f.pad_integral(true, prefix, &pre_pad) } } @@ -1676,14 +1658,17 @@ mod test { assert_eq!(&format!("{}", _1_2), "1/2"); assert_eq!(&format!("{}", -_1_2), "-1/2"); // test negatives assert_eq!(&format!("{}", _0), "0"); - assert_eq!(&format!("{}", Ratio::from_integer(-2)), "-2"); + assert_eq!(&format!("{}", -_2), "-2"); assert_eq!(&format!("{:b}", _2), "10"); assert_eq!(&format!("{:b}", _1_2), "1/10"); assert_eq!(&format!("{:b}", _0), "0"); - assert_eq!(&format!("{:b}", Ratio::from_integer(2)), "10"); + assert_eq!(&format!("{:b}", _2), "10"); assert_eq!(&format!("{:#b}", _1_2), "0b1/0b10"); assert_eq!(&format!("{:010b}", _1_2), "0000001/10"); assert_eq!(&format!("{:#010b}", _1_2), "0b001/0b10"); + let half_i8: Ratio = Ratio::new(1_i8, 2_i8); + assert_eq!(&format!("{:b}", -half_i8), "11111111/10"); + assert_eq!(&format!("{:#b}", -half_i8), "0b11111111/0b10"); } mod arith { From 272053f7945d1c51d71b16c5a72d509fb6aeabfc Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Mon, 12 Aug 2019 18:04:24 -0700 Subject: [PATCH 07/26] add test cases --- src/lib.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 4424a11..f845684 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1426,7 +1426,26 @@ mod test { numer: -2, denom: 1, }; + pub const _8: Rational = Ratio { numer: 8, denom: 1 }; + pub const _15: Rational = Ratio { + numer: 15, + denom: 1, + }; + pub const _16: Rational = Ratio { + numer: 16, + denom: 1, + }; + pub const _1_2: Rational = Ratio { numer: 1, denom: 2 }; + pub const _1_8: Rational = Ratio { numer: 1, denom: 8 }; + pub const _1_15: Rational = Ratio { + numer: 1, + denom: 15, + }; + pub const _1_16: Rational = Ratio { + numer: 1, + denom: 16, + }; pub const _3_2: Rational = Ratio { numer: 3, denom: 2 }; pub const _5_2: Rational = Ratio { numer: 5, denom: 2 }; pub const _NEG1_2: Rational = Ratio { @@ -1662,13 +1681,43 @@ mod test { assert_eq!(&format!("{:b}", _2), "10"); assert_eq!(&format!("{:b}", _1_2), "1/10"); assert_eq!(&format!("{:b}", _0), "0"); - assert_eq!(&format!("{:b}", _2), "10"); assert_eq!(&format!("{:#b}", _1_2), "0b1/0b10"); assert_eq!(&format!("{:010b}", _1_2), "0000001/10"); assert_eq!(&format!("{:#010b}", _1_2), "0b001/0b10"); let half_i8: Ratio = Ratio::new(1_i8, 2_i8); assert_eq!(&format!("{:b}", -half_i8), "11111111/10"); assert_eq!(&format!("{:#b}", -half_i8), "0b11111111/0b10"); + + assert_eq!(&format!("{:o}", _8), "10"); + assert_eq!(&format!("{:o}", _1_8), "1/10"); + assert_eq!(&format!("{:o}", _0), "0"); + assert_eq!(&format!("{:#o}", _1_8), "0o1/0o10"); + assert_eq!(&format!("{:010o}", _1_8), "0000001/10"); + assert_eq!(&format!("{:#010o}", _1_8), "0o001/0o10"); + assert_eq!(&format!("{:o}", -half_i8), "377/2"); + assert_eq!(&format!("{:#o}", -half_i8), "0o377/0o2"); + + assert_eq!(&format!("{:x}", _16), "10"); + assert_eq!(&format!("{:x}", _15), "f"); + assert_eq!(&format!("{:x}", _1_16), "1/10"); + assert_eq!(&format!("{:x}", _1_15), "1/f"); + assert_eq!(&format!("{:x}", _0), "0"); + assert_eq!(&format!("{:#x}", _1_16), "0x1/0x10"); + assert_eq!(&format!("{:010x}", _1_16), "0000001/10"); + assert_eq!(&format!("{:#010x}", _1_16), "0x001/0x10"); + assert_eq!(&format!("{:x}", -half_i8), "ff/2"); + assert_eq!(&format!("{:#x}", -half_i8), "0xff/0x2"); + + assert_eq!(&format!("{:X}", _16), "10"); + assert_eq!(&format!("{:X}", _15), "F"); + assert_eq!(&format!("{:X}", _1_16), "1/10"); + assert_eq!(&format!("{:X}", _1_15), "1/F"); + assert_eq!(&format!("{:X}", _0), "0"); + assert_eq!(&format!("{:#X}", _1_16), "0x1/0x10"); + assert_eq!(&format!("{:010X}", _1_16), "0000001/10"); + assert_eq!(&format!("{:#010X}", _1_16), "0x001/0x10"); + assert_eq!(&format!("{:X}", -half_i8), "FF/2"); + assert_eq!(&format!("{:#X}", -half_i8), "0xFF/0x2"); } mod arith { From 49b41c2040809d1793dea49b94dad7ecc2c44b4b Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Mon, 12 Aug 2019 18:26:33 -0700 Subject: [PATCH 08/26] remove extra trait bounds on Display traits --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f845684..61b1d4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1019,7 +1019,7 @@ where #[cfg(feature = "std")] impl Octal for Ratio where - T: Octal + Clone + Integer, + T: Octal + Eq + One, { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { @@ -1040,7 +1040,7 @@ where #[cfg(feature = "std")] impl Binary for Ratio where - T: Binary + Clone + Integer, + T: Binary + Eq + One, { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { @@ -1063,7 +1063,7 @@ where #[cfg(feature = "std")] impl LowerHex for Ratio where - T: LowerHex + Clone + Integer, + T: LowerHex + Eq + One, { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { @@ -1084,7 +1084,7 @@ where #[cfg(feature = "std")] impl UpperHex for Ratio where - T: UpperHex + Clone + Integer, + T: UpperHex + Eq + One, { /// Renders as `numer/denom`. If denom=1, renders as numer. fn fmt(&self, f: &mut Formatter) -> fmt::Result { From b4b1c8287bdfa0e60161deb30052ef8b66d66ea0 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Fri, 6 Dec 2019 02:47:51 -0800 Subject: [PATCH 09/26] switch formatting impls to using macros --- src/lib.rs | 119 ++++++++++++++++++----------------------------------- 1 file changed, 40 insertions(+), 79 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 61b1d4d..51858d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1016,91 +1016,38 @@ where } } -#[cfg(feature = "std")] -impl Octal for Ratio -where - T: Octal + Eq + One, -{ - /// Renders as `numer/denom`. If denom=1, renders as numer. - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let prefix = "0o"; - let pre_pad = if self.denom.is_one() { - format!("{:o}", self.numer) - } else { - if f.alternate() { - format!("{:o}/{:#o}", self.numer, self.denom) - } else { - format!("{:o}/{:o}", self.numer, self.denom) +macro_rules! impl_formatting { + ($trait:ident, $prefix:expr, $fmt_str:expr, $fmt_alt:expr) => { + impl $trait for Ratio { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let pre_pad = if self.denom.is_one() { + format!($fmt_str, self.numer) + } else { + if f.alternate() { + format!(concat!($fmt_str, "/", $fmt_alt), self.numer, self.denom) + } else { + format!(concat!($fmt_str, "/", $fmt_str), self.numer, self.denom) + } + }; + f.pad_integral(true, $prefix, &pre_pad) } - }; - f.pad_integral(true, prefix, &pre_pad) - } + } + + }; } #[cfg(feature = "std")] -impl Binary for Ratio -where - T: Binary + Eq + One, -{ - /// Renders as `numer/denom`. If denom=1, renders as numer. - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let prefix = "0b"; - let pre_pad = if self.denom.is_one() { - format!("{:b}", self.numer) - } else { - if f.alternate() { - format!("{:b}/{:#b}", self.numer, self.denom) - } else { - format!("{:b}/{:b}", self.numer, self.denom) - } - }; - // negative numbers are printed as two's compliment - // with no negative sign - f.pad_integral(true, prefix, &pre_pad) - } -} - +impl_formatting!(Octal, "0o", "{:o}", "{:#o}"); #[cfg(feature = "std")] -impl LowerHex for Ratio -where - T: LowerHex + Eq + One, -{ - /// Renders as `numer/denom`. If denom=1, renders as numer. - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let prefix = "0x"; - let pre_pad = if self.denom.is_one() { - format!("{:x}", self.numer) - } else { - if f.alternate() { - format!("{:x}/{:#x}", self.numer, self.denom) - } else { - format!("{:x}/{:x}", self.numer, self.denom) - } - }; - f.pad_integral(true, prefix, &pre_pad) - } -} - +impl_formatting!(Binary, "0b", "{:b}", "{:#b}"); #[cfg(feature = "std")] -impl UpperHex for Ratio -where - T: UpperHex + Eq + One, -{ - /// Renders as `numer/denom`. If denom=1, renders as numer. - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let prefix = "0x"; - let pre_pad = if self.denom.is_one() { - format!("{:X}", self.numer) - } else { - if f.alternate() { - format!("{:X}/{:#X}", self.numer, self.denom) - } else { - format!("{:X}/{:X}", self.numer, self.denom) - } - }; - f.pad_integral(true, prefix, &pre_pad) - } -} +impl_formatting!(LowerHex, "0x", "{:x}", "{:#x}"); +#[cfg(feature = "std")] +impl_formatting!(UpperHex, "0x", "{:X}", "{:#X}"); +#[cfg(feature = "std")] +impl_formatting!(LowerExp, "", "{:e}", "{:#e}"); +#[cfg(feature = "std")] +impl_formatting!(UpperExp, "", "{:E}", "{:#E}"); impl FromStr for Ratio { type Err = ParseRatioError; @@ -1718,6 +1665,20 @@ mod test { assert_eq!(&format!("{:#010X}", _1_16), "0x001/0x10"); assert_eq!(&format!("{:X}", -half_i8), "FF/2"); assert_eq!(&format!("{:#X}", -half_i8), "0xFF/0x2"); + + assert_eq!(&format!("{:e}", Ratio{numer:1.0_f32, denom:1.0_f32}), "1e0"); + assert_eq!(&format!("{:#e}", Ratio{numer:1.0_f32, denom:1.0_f32}), "1e0"); + assert_eq!(&format!("{:e}", Ratio{numer:1000.0_f32, denom:1.0_f32}), "1e3"); + assert_eq!(&format!("{:#e}", Ratio{numer:1000.0_f32, denom:1.0_f32}), "1e3"); + assert_eq!(&format!("{:e}", Ratio{numer:1000.0_f32, denom:7.0_f32}), "1e3/7e0"); + assert_eq!(&format!("{:#e}", Ratio{numer:1000.0_f32, denom:7.0_f32}), "1e3/7e0"); + + assert_eq!(&format!("{:E}", Ratio{numer:1.0_f32, denom:1.0_f32}), "1E0"); + assert_eq!(&format!("{:#E}", Ratio{numer:1.0_f32, denom:1.0_f32}), "1E0"); + assert_eq!(&format!("{:E}", Ratio{numer:1000.0_f32, denom:1.0_f32}), "1E3"); + assert_eq!(&format!("{:#E}", Ratio{numer:1000.0_f32, denom:1.0_f32}), "1E3"); + assert_eq!(&format!("{:E}", Ratio{numer:1000.0_f32, denom:7.0_f32}), "1E3/7E0"); + assert_eq!(&format!("{:#E}", Ratio{numer:1000.0_f32, denom:7.0_f32}), "1E3/7E0"); } mod arith { From e582dfe8e1efa330507c54d2fde1ff3dead6775a Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Fri, 6 Dec 2019 03:25:05 -0800 Subject: [PATCH 10/26] improve Exp formatting traits' tests --- src/lib.rs | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 51858d9..0eed061 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1017,8 +1017,8 @@ where } macro_rules! impl_formatting { - ($trait:ident, $prefix:expr, $fmt_str:expr, $fmt_alt:expr) => { - impl $trait for Ratio { + ($fmt_trait:ident, $prefix:expr, $fmt_str:expr, $fmt_alt:expr) => { + impl $fmt_trait for Ratio { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let pre_pad = if self.denom.is_one() { format!($fmt_str, self.numer) @@ -1666,19 +1666,22 @@ mod test { assert_eq!(&format!("{:X}", -half_i8), "FF/2"); assert_eq!(&format!("{:#X}", -half_i8), "0xFF/0x2"); - assert_eq!(&format!("{:e}", Ratio{numer:1.0_f32, denom:1.0_f32}), "1e0"); - assert_eq!(&format!("{:#e}", Ratio{numer:1.0_f32, denom:1.0_f32}), "1e0"); - assert_eq!(&format!("{:e}", Ratio{numer:1000.0_f32, denom:1.0_f32}), "1e3"); - assert_eq!(&format!("{:#e}", Ratio{numer:1000.0_f32, denom:1.0_f32}), "1e3"); - assert_eq!(&format!("{:e}", Ratio{numer:1000.0_f32, denom:7.0_f32}), "1e3/7e0"); - assert_eq!(&format!("{:#e}", Ratio{numer:1000.0_f32, denom:7.0_f32}), "1e3/7e0"); - - assert_eq!(&format!("{:E}", Ratio{numer:1.0_f32, denom:1.0_f32}), "1E0"); - assert_eq!(&format!("{:#E}", Ratio{numer:1.0_f32, denom:1.0_f32}), "1E0"); - assert_eq!(&format!("{:E}", Ratio{numer:1000.0_f32, denom:1.0_f32}), "1E3"); - assert_eq!(&format!("{:#E}", Ratio{numer:1000.0_f32, denom:1.0_f32}), "1E3"); - assert_eq!(&format!("{:E}", Ratio{numer:1000.0_f32, denom:7.0_f32}), "1E3/7E0"); - assert_eq!(&format!("{:#E}", Ratio{numer:1000.0_f32, denom:7.0_f32}), "1E3/7E0"); + let _one_tehth_1_f = Ratio{numer:0.1_f32, denom:1.0_f32}; + let _1000_f = Ratio{numer:1000.0_f32, denom:1.0_f32}; + let _1_big_f = Ratio{numer:1.0_f32, denom:std::f32::MAX}; + assert_eq!(&format!("{:e}", _one_tehth_1_f ), "1e-1"); + assert_eq!(&format!("{:#e}", _one_tehth_1_f), "1e-1"); + assert_eq!(&format!("{:e}", _1000_f), "1e3"); + assert_eq!(&format!("{:#e}", _1000_f), "1e3"); + assert_eq!(&format!("{:e}", _1_big_f), "1e0/3.4028235e38"); + assert_eq!(&format!("{:#e}", _1_big_f), "1e0/3.4028235e38"); + + assert_eq!(&format!("{:E}", _one_tehth_1_f), "1E-1"); + assert_eq!(&format!("{:#E}", _one_tehth_1_f), "1E-1"); + assert_eq!(&format!("{:E}", _1000_f), "1E3"); + assert_eq!(&format!("{:#E}", _1000_f), "1E3"); + assert_eq!(&format!("{:E}", _1_big_f), "1E0/3.4028235E38"); + assert_eq!(&format!("{:#E}", _1_big_f), "1E0/3.4028235E38"); } mod arith { From f7aea8f11d454ebeff8d30255b39f3b8a2e72a7b Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Fri, 6 Dec 2019 03:43:50 -0800 Subject: [PATCH 11/26] remove dependency on std for Exp formatting traits --- src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0eed061..d0a40a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1668,20 +1668,20 @@ mod test { let _one_tehth_1_f = Ratio{numer:0.1_f32, denom:1.0_f32}; let _1000_f = Ratio{numer:1000.0_f32, denom:1.0_f32}; - let _1_big_f = Ratio{numer:1.0_f32, denom:std::f32::MAX}; + let _1_big_f = Ratio{numer:1.0_f32, denom:3.14159e38}; assert_eq!(&format!("{:e}", _one_tehth_1_f ), "1e-1"); assert_eq!(&format!("{:#e}", _one_tehth_1_f), "1e-1"); assert_eq!(&format!("{:e}", _1000_f), "1e3"); assert_eq!(&format!("{:#e}", _1000_f), "1e3"); - assert_eq!(&format!("{:e}", _1_big_f), "1e0/3.4028235e38"); - assert_eq!(&format!("{:#e}", _1_big_f), "1e0/3.4028235e38"); + assert_eq!(&format!("{:e}", _1_big_f), "1e0/3.14159e38"); + assert_eq!(&format!("{:#e}", _1_big_f), "1e0/3.14159e38"); assert_eq!(&format!("{:E}", _one_tehth_1_f), "1E-1"); assert_eq!(&format!("{:#E}", _one_tehth_1_f), "1E-1"); assert_eq!(&format!("{:E}", _1000_f), "1E3"); assert_eq!(&format!("{:#E}", _1000_f), "1E3"); - assert_eq!(&format!("{:E}", _1_big_f), "1E0/3.4028235E38"); - assert_eq!(&format!("{:#E}", _1_big_f), "1E0/3.4028235E38"); + assert_eq!(&format!("{:E}", _1_big_f), "1E0/3.14159E38"); + assert_eq!(&format!("{:#E}", _1_big_f), "1E0/3.14159E38"); } mod arith { From c7ea97ac003a248ca75c1f4442cfaa2cd6e20fa4 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Fri, 6 Dec 2019 03:54:18 -0800 Subject: [PATCH 12/26] appease rustfmt --- src/lib.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d0a40a7..f6ea354 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1032,7 +1032,6 @@ macro_rules! impl_formatting { f.pad_integral(true, $prefix, &pre_pad) } } - }; } @@ -1666,10 +1665,19 @@ mod test { assert_eq!(&format!("{:X}", -half_i8), "FF/2"); assert_eq!(&format!("{:#X}", -half_i8), "0xFF/0x2"); - let _one_tehth_1_f = Ratio{numer:0.1_f32, denom:1.0_f32}; - let _1000_f = Ratio{numer:1000.0_f32, denom:1.0_f32}; - let _1_big_f = Ratio{numer:1.0_f32, denom:3.14159e38}; - assert_eq!(&format!("{:e}", _one_tehth_1_f ), "1e-1"); + let _one_tehth_1_f = Ratio { + numer: 0.1_f32, + denom: 1.0_f32, + }; + let _1000_f = Ratio { + numer: 1000.0_f32, + denom: 1.0_f32, + }; + let _1_big_f = Ratio { + numer: 1.0_f32, + denom: 3.14159e38, + }; + assert_eq!(&format!("{:e}", _one_tehth_1_f), "1e-1"); assert_eq!(&format!("{:#e}", _one_tehth_1_f), "1e-1"); assert_eq!(&format!("{:e}", _1000_f), "1e3"); assert_eq!(&format!("{:#e}", _1000_f), "1e3"); From a66187957020fd99bc3740b737fedbb8142273e5 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Fri, 6 Dec 2019 05:34:19 -0800 Subject: [PATCH 13/26] attempt to support no_std --- src/lib.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f6ea354..a30105b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1019,6 +1019,7 @@ where macro_rules! impl_formatting { ($fmt_trait:ident, $prefix:expr, $fmt_str:expr, $fmt_alt:expr) => { impl $fmt_trait for Ratio { + #[cfg(feature = "std")] fn fmt(&self, f: &mut Formatter) -> fmt::Result { let pre_pad = if self.denom.is_one() { format!($fmt_str, self.numer) @@ -1031,21 +1032,27 @@ macro_rules! impl_formatting { }; f.pad_integral(true, $prefix, &pre_pad) } + #[cfg(not(feature = "std"))] + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + if self.denom.is_one() { + write!(f, $fmt_str, self.numer) + } else { + if f.alternate() { + write!(f, concat!($fmt_str, "/", $fmt_alt), self.numer, self.denom) + } else { + write!(f, concat!($fmt_str, "/", $fmt_str), self.numer, self.denom) + } + } + } } }; } -#[cfg(feature = "std")] impl_formatting!(Octal, "0o", "{:o}", "{:#o}"); -#[cfg(feature = "std")] impl_formatting!(Binary, "0b", "{:b}", "{:#b}"); -#[cfg(feature = "std")] impl_formatting!(LowerHex, "0x", "{:x}", "{:#x}"); -#[cfg(feature = "std")] impl_formatting!(UpperHex, "0x", "{:X}", "{:#X}"); -#[cfg(feature = "std")] impl_formatting!(LowerExp, "", "{:e}", "{:#e}"); -#[cfg(feature = "std")] impl_formatting!(UpperExp, "", "{:E}", "{:#E}"); impl FromStr for Ratio { @@ -1610,6 +1617,70 @@ mod test { assert!(!_NEG1_2.is_integer()); } + #[cfg(not(feature = "std"))] + use core::fmt; + #[cfg(not(feature = "std"))] + use fmt::Write; + #[cfg(not(feature = "std"))] + const TESTER_BUF_SIZE: usize = 32; + #[cfg(not(feature = "std"))] + #[derive(Debug)] + struct NoStdTester { + cursor: usize, + buf: [u8; TESTER_BUF_SIZE], + } + + #[cfg(not(feature = "std"))] + impl NoStdTester { + fn new() -> NoStdTester { + NoStdTester { + buf: [0; TESTER_BUF_SIZE], + cursor: 0, + } + } + + fn clear(&mut self) { + self.cursor = 0; + } + } + + #[cfg(not(feature = "std"))] + impl Write for NoStdTester { + fn write_str(&mut self, s: &str) -> fmt::Result { + for byte in s.bytes() { + self.buf[self.cursor] = byte; + self.cursor += 1; + if self.cursor >= self.buf.len() { + return Err(fmt::Error {}); + } + } + Ok(()) + } + } + + #[cfg(not(feature = "std"))] + impl PartialEq for NoStdTester { + fn eq(&self, other: &str) -> bool { + let other = other.as_bytes(); + for index in 0..self.cursor { + if self.buf.get(index) != other.get(index) { + return false; + } + } + true + } + } + + #[test] + #[cfg(not(feature = "std"))] + fn test_show() { + let err_msg = "Formatted output too long"; + let mut tester = NoStdTester::new(); + write!(tester, "{:o}", _8).expect(err_msg); + assert_eq!(tester, *"10"); + tester.clear(); + } + #[test] #[cfg(feature = "std")] fn test_show() { From aa1c26b4e00c7a4a2d51da2b772c5b814d4facd3 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Tue, 18 Feb 2020 18:26:06 -0800 Subject: [PATCH 14/26] Add breaking change to Display --- src/lib.rs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a30105b..ff752c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,6 @@ #![allow(clippy::suspicious_arithmetic_impl)] #![allow(clippy::suspicious_op_assign_impl)] - #[cfg(feature = "std")] #[macro_use] extern crate std; @@ -1002,20 +1001,6 @@ impl Signed for Ratio { } // String conversions -impl Display for Ratio -where - T: Display + Eq + One, -{ - /// Renders as `numer/denom`. If denom=1, renders as numer. - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - if self.denom.is_one() { - write!(f, "{}", self.numer) - } else { - write!(f, "{}/{}", self.numer, self.denom) - } - } -} - macro_rules! impl_formatting { ($fmt_trait:ident, $prefix:expr, $fmt_str:expr, $fmt_alt:expr) => { impl $fmt_trait for Ratio { @@ -1048,6 +1033,7 @@ macro_rules! impl_formatting { }; } +impl_formatting!(Display, "", "{}", "{:#}"); impl_formatting!(Octal, "0o", "{:o}", "{:#o}"); impl_formatting!(Binary, "0b", "{:b}", "{:#b}"); impl_formatting!(LowerHex, "0x", "{:x}", "{:#x}"); From bb3a544384f3d1a74f57c29325dd0e501dd3f6c8 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Tue, 18 Feb 2020 18:40:17 -0800 Subject: [PATCH 15/26] fix imports in rust 1.31 --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ff752c1..e5cbd28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ extern crate std; use core::cmp; use core::fmt; +use core::fmt::{Binary, Display, Formatter, LowerExp, LowerHex, Octal, UpperExp, UpperHex}; use core::hash::{Hash, Hasher}; use core::ops::{Add, Div, Mul, Neg, Rem, Sub}; use core::str::FromStr; @@ -43,7 +44,6 @@ use num_traits::{ }; mod pow; -use fmt::{Binary, Display, Formatter, LowerHex, Octal, UpperHex}; /// Represents the ratio between two numbers. #[derive(Copy, Clone, Debug)] From 570e40153a47aec9080c7b8ef3ed191c0b81cdb0 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Thu, 20 Feb 2020 00:48:07 -0800 Subject: [PATCH 16/26] fix typo Co-Authored-By: Josh Stone --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index e5cbd28..93e8f7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1722,7 +1722,7 @@ mod test { assert_eq!(&format!("{:X}", -half_i8), "FF/2"); assert_eq!(&format!("{:#X}", -half_i8), "0xFF/0x2"); - let _one_tehth_1_f = Ratio { + let _one_tenth_1_f = Ratio { numer: 0.1_f32, denom: 1.0_f32, }; From 37804b4bf1ac6a5f6e1b99c168be26f036cd54d0 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Thu, 20 Feb 2020 01:03:12 -0800 Subject: [PATCH 17/26] maybe fix imports --- src/lib.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 93e8f7a..db8e961 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1604,9 +1604,7 @@ mod test { } #[cfg(not(feature = "std"))] - use core::fmt; - #[cfg(not(feature = "std"))] - use fmt::Write; + use core::fmt::{self, Write}; #[cfg(not(feature = "std"))] const TESTER_BUF_SIZE: usize = 32; #[cfg(not(feature = "std"))] @@ -1734,15 +1732,15 @@ mod test { numer: 1.0_f32, denom: 3.14159e38, }; - assert_eq!(&format!("{:e}", _one_tehth_1_f), "1e-1"); - assert_eq!(&format!("{:#e}", _one_tehth_1_f), "1e-1"); + assert_eq!(&format!("{:e}", _one_tenth_1_f), "1e-1"); + assert_eq!(&format!("{:#e}", _one_tenth_1_f), "1e-1"); assert_eq!(&format!("{:e}", _1000_f), "1e3"); assert_eq!(&format!("{:#e}", _1000_f), "1e3"); assert_eq!(&format!("{:e}", _1_big_f), "1e0/3.14159e38"); assert_eq!(&format!("{:#e}", _1_big_f), "1e0/3.14159e38"); - assert_eq!(&format!("{:E}", _one_tehth_1_f), "1E-1"); - assert_eq!(&format!("{:#E}", _one_tehth_1_f), "1E-1"); + assert_eq!(&format!("{:E}", _one_tenth_1_f), "1E-1"); + assert_eq!(&format!("{:#E}", _one_tenth_1_f), "1E-1"); assert_eq!(&format!("{:E}", _1000_f), "1E3"); assert_eq!(&format!("{:#E}", _1000_f), "1E3"); assert_eq!(&format!("{:E}", _1_big_f), "1E0/3.14159E38"); From ac41f59dd8acec730bb0ed0416e798a796abe688 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Fri, 21 Feb 2020 02:28:24 -0800 Subject: [PATCH 18/26] improve no_std display tests --- src/lib.rs | 153 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 87 insertions(+), 66 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index db8e961..210d90c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1020,10 +1020,14 @@ macro_rules! impl_formatting { #[cfg(not(feature = "std"))] fn fmt(&self, f: &mut Formatter) -> fmt::Result { if self.denom.is_one() { - write!(f, $fmt_str, self.numer) + if f.alternate() { + write!(f, $fmt_alt, self.numer) + } else { + write!(f, $fmt_str, self.numer) + } } else { if f.alternate() { - write!(f, concat!($fmt_str, "/", $fmt_alt), self.numer, self.denom) + write!(f, concat!($fmt_alt, "/", $fmt_alt), self.numer, self.denom) } else { write!(f, concat!($fmt_str, "/", $fmt_str), self.numer, self.denom) } @@ -1606,26 +1610,28 @@ mod test { #[cfg(not(feature = "std"))] use core::fmt::{self, Write}; #[cfg(not(feature = "std"))] - const TESTER_BUF_SIZE: usize = 32; - #[cfg(not(feature = "std"))] #[derive(Debug)] struct NoStdTester { cursor: usize, - buf: [u8; TESTER_BUF_SIZE], + buf: [u8; NoStdTester::BUF_SIZE], } #[cfg(not(feature = "std"))] impl NoStdTester { fn new() -> NoStdTester { NoStdTester { - buf: [0; TESTER_BUF_SIZE], + buf: [0; Self::BUF_SIZE], cursor: 0, } } fn clear(&mut self) { + self.buf = [0; Self::BUF_SIZE]; self.cursor = 0; } + + const WRITE_ERR: &'static str = "Formatted output too long"; + const BUF_SIZE: usize = 32; } #[cfg(not(feature = "std"))] @@ -1655,18 +1661,23 @@ mod test { } } - #[test] - #[cfg(not(feature = "std"))] - fn test_show() { - let err_msg = "Formatted output too long"; - let mut tester = NoStdTester::new(); - write!(tester, "{:o}", _8).expect(err_msg); - assert_eq!(tester, *"10"); - tester.clear(); + macro_rules! assert_fmt_eq { + ($fmt_args:expr, $string:expr) => { + #[cfg(not(feature = "std"))] + { + let mut tester = NoStdTester::new(); + write!(tester, "{}", $fmt_args).expect(NoStdTester::WRITE_ERR); + assert_eq!(tester, *$string); + tester.clear(); + } + #[cfg(feature = "std")] + { + assert_eq!(std::fmt::format($fmt_args), $string); + } + }; } #[test] - #[cfg(feature = "std")] fn test_show() { // Test: // :b :o :x, :X, :? @@ -1674,51 +1685,61 @@ mod test { // positive and negative // padding // does not test precision (i.e. truncation) - assert_eq!(&format!("{}", _2), "2"); - assert_eq!(&format!("{}", _1_2), "1/2"); - assert_eq!(&format!("{}", -_1_2), "-1/2"); // test negatives - assert_eq!(&format!("{}", _0), "0"); - assert_eq!(&format!("{}", -_2), "-2"); - assert_eq!(&format!("{:b}", _2), "10"); - assert_eq!(&format!("{:b}", _1_2), "1/10"); - assert_eq!(&format!("{:b}", _0), "0"); - assert_eq!(&format!("{:#b}", _1_2), "0b1/0b10"); + assert_fmt_eq!(format_args!("{}", _2), "2"); + assert_fmt_eq!(format_args!("{}", _1_2), "1/2"); + assert_fmt_eq!(format_args!("{}", -_1_2), "-1/2"); // test negatives + assert_fmt_eq!(format_args!("{}", _0), "0"); + assert_fmt_eq!(format_args!("{}", -_2), "-2"); + assert_fmt_eq!(format_args!("{:b}", _2), "10"); + assert_fmt_eq!(format_args!("{:#b}", _2), "0b10"); + assert_fmt_eq!(format_args!("{:b}", _1_2), "1/10"); + assert_fmt_eq!(format_args!("{:b}", _0), "0"); + assert_fmt_eq!(format_args!("{:#b}", _1_2), "0b1/0b10"); + //no std does not support padding + #[cfg(feature = "std")] assert_eq!(&format!("{:010b}", _1_2), "0000001/10"); + #[cfg(feature = "std")] assert_eq!(&format!("{:#010b}", _1_2), "0b001/0b10"); let half_i8: Ratio = Ratio::new(1_i8, 2_i8); - assert_eq!(&format!("{:b}", -half_i8), "11111111/10"); - assert_eq!(&format!("{:#b}", -half_i8), "0b11111111/0b10"); - - assert_eq!(&format!("{:o}", _8), "10"); - assert_eq!(&format!("{:o}", _1_8), "1/10"); - assert_eq!(&format!("{:o}", _0), "0"); - assert_eq!(&format!("{:#o}", _1_8), "0o1/0o10"); + assert_fmt_eq!(format_args!("{:b}", -half_i8), "11111111/10"); + assert_fmt_eq!(format_args!("{:#b}", -half_i8), "0b11111111/0b10"); + + assert_fmt_eq!(format_args!("{:o}", _8), "10"); + assert_fmt_eq!(format_args!("{:o}", _1_8), "1/10"); + assert_fmt_eq!(format_args!("{:o}", _0), "0"); + assert_fmt_eq!(format_args!("{:#o}", _1_8), "0o1/0o10"); + #[cfg(feature = "std")] assert_eq!(&format!("{:010o}", _1_8), "0000001/10"); + #[cfg(feature = "std")] assert_eq!(&format!("{:#010o}", _1_8), "0o001/0o10"); - assert_eq!(&format!("{:o}", -half_i8), "377/2"); - assert_eq!(&format!("{:#o}", -half_i8), "0o377/0o2"); - - assert_eq!(&format!("{:x}", _16), "10"); - assert_eq!(&format!("{:x}", _15), "f"); - assert_eq!(&format!("{:x}", _1_16), "1/10"); - assert_eq!(&format!("{:x}", _1_15), "1/f"); - assert_eq!(&format!("{:x}", _0), "0"); - assert_eq!(&format!("{:#x}", _1_16), "0x1/0x10"); + assert_fmt_eq!(format_args!("{:o}", -half_i8), "377/2"); + assert_fmt_eq!(format_args!("{:#o}", -half_i8), "0o377/0o2"); + + assert_fmt_eq!(format_args!("{:x}", _16), "10"); + assert_fmt_eq!(format_args!("{:x}", _15), "f"); + assert_fmt_eq!(format_args!("{:x}", _1_16), "1/10"); + assert_fmt_eq!(format_args!("{:x}", _1_15), "1/f"); + assert_fmt_eq!(format_args!("{:x}", _0), "0"); + assert_fmt_eq!(format_args!("{:#x}", _1_16), "0x1/0x10"); + #[cfg(feature = "std")] assert_eq!(&format!("{:010x}", _1_16), "0000001/10"); + #[cfg(feature = "std")] assert_eq!(&format!("{:#010x}", _1_16), "0x001/0x10"); - assert_eq!(&format!("{:x}", -half_i8), "ff/2"); - assert_eq!(&format!("{:#x}", -half_i8), "0xff/0x2"); - - assert_eq!(&format!("{:X}", _16), "10"); - assert_eq!(&format!("{:X}", _15), "F"); - assert_eq!(&format!("{:X}", _1_16), "1/10"); - assert_eq!(&format!("{:X}", _1_15), "1/F"); - assert_eq!(&format!("{:X}", _0), "0"); - assert_eq!(&format!("{:#X}", _1_16), "0x1/0x10"); - assert_eq!(&format!("{:010X}", _1_16), "0000001/10"); - assert_eq!(&format!("{:#010X}", _1_16), "0x001/0x10"); - assert_eq!(&format!("{:X}", -half_i8), "FF/2"); - assert_eq!(&format!("{:#X}", -half_i8), "0xFF/0x2"); + assert_fmt_eq!(format_args!("{:x}", -half_i8), "ff/2"); + assert_fmt_eq!(format_args!("{:#x}", -half_i8), "0xff/0x2"); + + assert_fmt_eq!(format_args!("{:X}", _16), "10"); + assert_fmt_eq!(format_args!("{:X}", _15), "F"); + assert_fmt_eq!(format_args!("{:X}", _1_16), "1/10"); + assert_fmt_eq!(format_args!("{:X}", _1_15), "1/F"); + assert_fmt_eq!(format_args!("{:X}", _0), "0"); + assert_fmt_eq!(format_args!("{:#X}", _1_16), "0x1/0x10"); + #[cfg(feature = "std")] + assert_eq!(format!("{:010X}", _1_16), "0000001/10"); + #[cfg(feature = "std")] + assert_eq!(format!("{:#010X}", _1_16), "0x001/0x10"); + assert_fmt_eq!(format_args!("{:X}", -half_i8), "FF/2"); + assert_fmt_eq!(format_args!("{:#X}", -half_i8), "0xFF/0x2"); let _one_tenth_1_f = Ratio { numer: 0.1_f32, @@ -1732,19 +1753,19 @@ mod test { numer: 1.0_f32, denom: 3.14159e38, }; - assert_eq!(&format!("{:e}", _one_tenth_1_f), "1e-1"); - assert_eq!(&format!("{:#e}", _one_tenth_1_f), "1e-1"); - assert_eq!(&format!("{:e}", _1000_f), "1e3"); - assert_eq!(&format!("{:#e}", _1000_f), "1e3"); - assert_eq!(&format!("{:e}", _1_big_f), "1e0/3.14159e38"); - assert_eq!(&format!("{:#e}", _1_big_f), "1e0/3.14159e38"); - - assert_eq!(&format!("{:E}", _one_tenth_1_f), "1E-1"); - assert_eq!(&format!("{:#E}", _one_tenth_1_f), "1E-1"); - assert_eq!(&format!("{:E}", _1000_f), "1E3"); - assert_eq!(&format!("{:#E}", _1000_f), "1E3"); - assert_eq!(&format!("{:E}", _1_big_f), "1E0/3.14159E38"); - assert_eq!(&format!("{:#E}", _1_big_f), "1E0/3.14159E38"); + assert_fmt_eq!(format_args!("{:e}", _one_tenth_1_f), "1e-1"); + assert_fmt_eq!(format_args!("{:#e}", _one_tenth_1_f), "1e-1"); + assert_fmt_eq!(format_args!("{:e}", _1000_f), "1e3"); + assert_fmt_eq!(format_args!("{:#e}", _1000_f), "1e3"); + assert_fmt_eq!(format_args!("{:e}", _1_big_f), "1e0/3.14159e38"); + assert_fmt_eq!(format_args!("{:#e}", _1_big_f), "1e0/3.14159e38"); + + assert_fmt_eq!(format_args!("{:E}", _one_tenth_1_f), "1E-1"); + assert_fmt_eq!(format_args!("{:#E}", _one_tenth_1_f), "1E-1"); + assert_fmt_eq!(format_args!("{:E}", _1000_f), "1E3"); + assert_fmt_eq!(format_args!("{:#E}", _1000_f), "1E3"); + assert_fmt_eq!(format_args!("{:E}", _1_big_f), "1E0/3.14159E38"); + assert_fmt_eq!(format_args!("{:#E}", _1_big_f), "1E0/3.14159E38"); } mod arith { From c847d584ea35c6973bdd4569bf1c2ac64d099fc8 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Thu, 12 Mar 2020 19:10:51 -0700 Subject: [PATCH 19/26] properly handle formatter.sign_plus() previously formatting would fail the following test case format!("{:+}", Ratio:new(-2, 1)), returning "+-2", utter nonsense. This fix adds a check for a negative sign (dash) in an intermediate string representation, removing it if found. no_std targets previously didn't support sign_plus(), but now do. --- src/lib.rs | 79 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 210d90c..f03cc43 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1003,7 +1003,7 @@ impl Signed for Ratio { // String conversions macro_rules! impl_formatting { ($fmt_trait:ident, $prefix:expr, $fmt_str:expr, $fmt_alt:expr) => { - impl $fmt_trait for Ratio { + impl $fmt_trait for Ratio { #[cfg(feature = "std")] fn fmt(&self, f: &mut Formatter) -> fmt::Result { let pre_pad = if self.denom.is_one() { @@ -1015,21 +1015,51 @@ macro_rules! impl_formatting { format!(concat!($fmt_str, "/", $fmt_str), self.numer, self.denom) } }; - f.pad_integral(true, $prefix, &pre_pad) + if f.sign_plus() { + let pre_pad = pre_pad.trim_start_matches('-'); + let non_negative = self.numer >= T::zero(); + f.pad_integral(non_negative, $prefix, pre_pad) + } else { + f.pad_integral(true, $prefix, &pre_pad) + } } #[cfg(not(feature = "std"))] fn fmt(&self, f: &mut Formatter) -> fmt::Result { - if self.denom.is_one() { - if f.alternate() { - write!(f, $fmt_alt, self.numer) + if f.sign_plus() && self.numer >= T::zero() { + if self.denom.is_one() { + if f.alternate() { + write!(f, concat!("+", $fmt_alt), self.numer) + } else { + write!(f, concat!("+", $fmt_str), self.numer) + } } else { - write!(f, $fmt_str, self.numer) + if f.alternate() { + write!( + f, + concat!("+", $fmt_alt, "/", $fmt_alt), + self.numer, self.denom + ) + } else { + write!( + f, + concat!("+", $fmt_str, "/", $fmt_str), + self.numer, self.denom + ) + } } } else { - if f.alternate() { - write!(f, concat!($fmt_alt, "/", $fmt_alt), self.numer, self.denom) + if self.denom.is_one() { + if f.alternate() { + write!(f, $fmt_alt, self.numer) + } else { + write!(f, $fmt_str, self.numer) + } } else { - write!(f, concat!($fmt_str, "/", $fmt_str), self.numer, self.denom) + if f.alternate() { + write!(f, concat!($fmt_alt, "/", $fmt_alt), self.numer, self.denom) + } else { + write!(f, concat!($fmt_str, "/", $fmt_str), self.numer, self.denom) + } } } } @@ -1686,13 +1716,18 @@ mod test { // padding // does not test precision (i.e. truncation) assert_fmt_eq!(format_args!("{}", _2), "2"); + assert_fmt_eq!(format_args!("{:+}", _2), "+2"); + assert_fmt_eq!(format_args!("{:-}", _2), "2"); assert_fmt_eq!(format_args!("{}", _1_2), "1/2"); assert_fmt_eq!(format_args!("{}", -_1_2), "-1/2"); // test negatives assert_fmt_eq!(format_args!("{}", _0), "0"); assert_fmt_eq!(format_args!("{}", -_2), "-2"); + assert_fmt_eq!(format_args!("{:+}", -_2), "-2"); assert_fmt_eq!(format_args!("{:b}", _2), "10"); assert_fmt_eq!(format_args!("{:#b}", _2), "0b10"); assert_fmt_eq!(format_args!("{:b}", _1_2), "1/10"); + assert_fmt_eq!(format_args!("{:+b}", _1_2), "+1/10"); + assert_fmt_eq!(format_args!("{:-b}", _1_2), "1/10"); assert_fmt_eq!(format_args!("{:b}", _0), "0"); assert_fmt_eq!(format_args!("{:#b}", _1_2), "0b1/0b10"); //no std does not support padding @@ -1753,19 +1788,19 @@ mod test { numer: 1.0_f32, denom: 3.14159e38, }; - assert_fmt_eq!(format_args!("{:e}", _one_tenth_1_f), "1e-1"); - assert_fmt_eq!(format_args!("{:#e}", _one_tenth_1_f), "1e-1"); - assert_fmt_eq!(format_args!("{:e}", _1000_f), "1e3"); - assert_fmt_eq!(format_args!("{:#e}", _1000_f), "1e3"); - assert_fmt_eq!(format_args!("{:e}", _1_big_f), "1e0/3.14159e38"); - assert_fmt_eq!(format_args!("{:#e}", _1_big_f), "1e0/3.14159e38"); - - assert_fmt_eq!(format_args!("{:E}", _one_tenth_1_f), "1E-1"); - assert_fmt_eq!(format_args!("{:#E}", _one_tenth_1_f), "1E-1"); - assert_fmt_eq!(format_args!("{:E}", _1000_f), "1E3"); - assert_fmt_eq!(format_args!("{:#E}", _1000_f), "1E3"); - assert_fmt_eq!(format_args!("{:E}", _1_big_f), "1E0/3.14159E38"); - assert_fmt_eq!(format_args!("{:#E}", _1_big_f), "1E0/3.14159E38"); + // assert_fmt_eq!(format_args!("{:e}", _one_tenth_1_f), "1e-1"); + // assert_fmt_eq!(format_args!("{:#e}", _one_tenth_1_f), "1e-1"); + // assert_fmt_eq!(format_args!("{:e}", _1000_f), "1e3"); + // assert_fmt_eq!(format_args!("{:#e}", _1000_f), "1e3"); + // assert_fmt_eq!(format_args!("{:e}", _1_big_f), "1e0/3.14159e38"); + // assert_fmt_eq!(format_args!("{:#e}", _1_big_f), "1e0/3.14159e38"); + + // assert_fmt_eq!(format_args!("{:E}", _one_tenth_1_f), "1E-1"); + // assert_fmt_eq!(format_args!("{:#E}", _one_tenth_1_f), "1E-1"); + // assert_fmt_eq!(format_args!("{:E}", _1000_f), "1E3"); + // assert_fmt_eq!(format_args!("{:#E}", _1000_f), "1E3"); + // assert_fmt_eq!(format_args!("{:E}", _1_big_f), "1E0/3.14159E38"); + // assert_fmt_eq!(format_args!("{:#E}", _1_big_f), "1E0/3.14159E38"); } mod arith { From c67e0aec768f5a973501d3ed8f9ea22e6e53ba0a Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Thu, 12 Mar 2020 22:56:45 -0700 Subject: [PATCH 20/26] add parsing rust version from rustc to ci script Now the correct features will be enabled when running the ci script outside of ci environments. --- ci/test_full.sh | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/ci/test_full.sh b/ci/test_full.sh index e1bc53d..c16df7f 100755 --- a/ci/test_full.sh +++ b/ci/test_full.sh @@ -1,12 +1,28 @@ #!/bin/bash -set -ex +set -e -echo Testing num-rational on rustc ${TRAVIS_RUST_VERSION} +get_rust_version() { + local array=($(rustc --version)); + echo "${array[1]}"; + return 0; +} + +if [ -z ${TRAVIS+x} ] +then RUST_VERSION=$(get_rust_version) # we're not in travis +else RUST_VERSION=$TRAVIS_RUST_VERSION # we're in travis +fi + +if [ -z ${RUST_VERSION} ] +then echo "WARNING: RUST_VERSION is undefined or empty string" 1>&2 +else echo Testing num-rational on rustc ${RUST_VERSION} +fi + +set -x STD_FEATURES="bigint-std serde" -case "$TRAVIS_RUST_VERSION" in +case "$RUST_VERSION" in 1.3[1-5].*) NO_STD_FEATURES="serde" ;; *) NO_STD_FEATURES="bigint serde" ;; esac From 09e859ac8c8df3fe856319645ed37a6610a81476 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Thu, 12 Mar 2020 23:04:39 -0700 Subject: [PATCH 21/26] fix shellcheck suggestions on ci script --- ci/test_full.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/test_full.sh b/ci/test_full.sh index c16df7f..7234361 100755 --- a/ci/test_full.sh +++ b/ci/test_full.sh @@ -13,9 +13,9 @@ then RUST_VERSION=$(get_rust_version) # we're not in travis else RUST_VERSION=$TRAVIS_RUST_VERSION # we're in travis fi -if [ -z ${RUST_VERSION} ] +if [ -z "${RUST_VERSION}" ] then echo "WARNING: RUST_VERSION is undefined or empty string" 1>&2 -else echo Testing num-rational on rustc ${RUST_VERSION} +else echo Testing num-rational on rustc "${RUST_VERSION}" fi set -x From e985a0f777fdd98c85b874700ee5fa102514cee8 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Tue, 17 Mar 2020 17:20:04 -0700 Subject: [PATCH 22/26] reduce code duplication in formatting code --- src/lib.rs | 51 ++++++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f03cc43..677670c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1025,41 +1025,30 @@ macro_rules! impl_formatting { } #[cfg(not(feature = "std"))] fn fmt(&self, f: &mut Formatter) -> fmt::Result { - if f.sign_plus() && self.numer >= T::zero() { - if self.denom.is_one() { - if f.alternate() { - write!(f, concat!("+", $fmt_alt), self.numer) - } else { - write!(f, concat!("+", $fmt_str), self.numer) - } + plus = if f.sign_plus() && self.numer >= T::zero() { + "+" + } else { + "" + }; + if self.denom.is_one() { + if f.alternate() { + write!(f, concat!("{}", $fmt_alt), plus, self.numer) } else { - if f.alternate() { - write!( - f, - concat!("+", $fmt_alt, "/", $fmt_alt), - self.numer, self.denom - ) - } else { - write!( - f, - concat!("+", $fmt_str, "/", $fmt_str), - self.numer, self.denom - ) - } + write!(f, concat!("{}", $fmt_str), plus, self.numer) } } else { - if self.denom.is_one() { - if f.alternate() { - write!(f, $fmt_alt, self.numer) - } else { - write!(f, $fmt_str, self.numer) - } + if f.alternate() { + write!( + f, + concat!("{}", $fmt_alt, "/", $fmt_alt), + plus, self.numer, self.denom + ) } else { - if f.alternate() { - write!(f, concat!($fmt_alt, "/", $fmt_alt), self.numer, self.denom) - } else { - write!(f, concat!($fmt_str, "/", $fmt_str), self.numer, self.denom) - } + write!( + f, + concat!("{}", $fmt_str, "/", $fmt_str), + plus, self.numer, self.denom + ) } } } From 92064f3ea92604f1df791072e91fb4f3ff172fcf Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Tue, 17 Mar 2020 18:06:11 -0700 Subject: [PATCH 23/26] fix displaying negatives with padding --- src/lib.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 677670c..3c06ae0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1015,13 +1015,15 @@ macro_rules! impl_formatting { format!(concat!($fmt_str, "/", $fmt_str), self.numer, self.denom) } }; - if f.sign_plus() { - let pre_pad = pre_pad.trim_start_matches('-'); - let non_negative = self.numer >= T::zero(); - f.pad_integral(non_negative, $prefix, pre_pad) - } else { - f.pad_integral(true, $prefix, &pre_pad) - } + //TODO: replace with strip_prefix, when stabalized + let (pre_pad, non_negative) = { + if pre_pad.starts_with("-") { + (&pre_pad[1..], false) + } else { + (&pre_pad[..], true) + } + }; + f.pad_integral(non_negative, $prefix, pre_pad) } #[cfg(not(feature = "std"))] fn fmt(&self, f: &mut Formatter) -> fmt::Result { @@ -1727,6 +1729,8 @@ mod test { let half_i8: Ratio = Ratio::new(1_i8, 2_i8); assert_fmt_eq!(format_args!("{:b}", -half_i8), "11111111/10"); assert_fmt_eq!(format_args!("{:#b}", -half_i8), "0b11111111/0b10"); + #[cfg(feature = "std")] + assert_eq!(&format!("{:05}", Ratio::new(-1_i8, 1_i8)), "-0001"); assert_fmt_eq!(format_args!("{:o}", _8), "10"); assert_fmt_eq!(format_args!("{:o}", _1_8), "1/10"); From 92f8027972ac2c0ead9986ddc8a8dbf15fa9487b Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Tue, 17 Mar 2020 21:53:49 -0700 Subject: [PATCH 24/26] Improve testing exponential formatting Use autofcg to detect if integers support exponential formatting. If so, autocfg sets the has_int_exp_fmt cfg, used in tests. --- Cargo.toml | 3 +++ build.rs | 20 ++++++++++++++++++++ src/lib.rs | 49 +++++++++++++++++++++++-------------------------- 3 files changed, 46 insertions(+), 26 deletions(-) create mode 100644 build.rs diff --git a/Cargo.toml b/Cargo.toml index 2edac38..0de431b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,3 +45,6 @@ default = ["bigint-std", "std"] std = ["num-integer/std", "num-traits/std"] bigint = ["num-bigint"] bigint-std = ["bigint", "num-bigint/std"] + +[build-dependencies] +autocfg = "1.0.0" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..b767337 --- /dev/null +++ b/build.rs @@ -0,0 +1,20 @@ +fn main() { + let ac = autocfg::new(); + if ac.probe_expression("format!(\"{:e}\", 0_i32)") { + if !(ac.probe_expression("format!(\"{:e}\", 0_i8)") && + ac.probe_expression("format!(\"{:e}\", 0_i16)") && + ac.probe_expression("format!(\"{:e}\", 0_i32)") && + ac.probe_expression("format!(\"{:e}\", 0_i64)") && + ac.probe_expression("format!(\"{:e}\", 0_i128)") && + ac.probe_expression("format!(\"{:e}\", 0_u8)") && + ac.probe_expression("format!(\"{:e}\", 0_u16)") && + ac.probe_expression("format!(\"{:e}\", 0_u32)") && + ac.probe_expression("format!(\"{:e}\", 0_u64)") && + ac.probe_expression("format!(\"{:e}\", 0_u128)")) { + panic!("Some integer types implement *Exp traits, but not others") + } + println!("cargo:rustc-cfg=has_int_exp_fmt"); + } + + autocfg::rerun_path(file!()); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 3c06ae0..d77306b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1027,7 +1027,7 @@ macro_rules! impl_formatting { } #[cfg(not(feature = "std"))] fn fmt(&self, f: &mut Formatter) -> fmt::Result { - plus = if f.sign_plus() && self.numer >= T::zero() { + let plus = if f.sign_plus() && self.numer >= T::zero() { "+" } else { "" @@ -1450,6 +1450,10 @@ mod test { numer: isize::MAX - 1, denom: 1, }; + pub const _BILLION: Rational = Ratio { + numer: 1_000_000_000, + denom: 1, + }; #[cfg(feature = "bigint")] pub fn to_big(n: Rational) -> BigRational { @@ -1769,31 +1773,24 @@ mod test { assert_fmt_eq!(format_args!("{:X}", -half_i8), "FF/2"); assert_fmt_eq!(format_args!("{:#X}", -half_i8), "0xFF/0x2"); - let _one_tenth_1_f = Ratio { - numer: 0.1_f32, - denom: 1.0_f32, - }; - let _1000_f = Ratio { - numer: 1000.0_f32, - denom: 1.0_f32, - }; - let _1_big_f = Ratio { - numer: 1.0_f32, - denom: 3.14159e38, - }; - // assert_fmt_eq!(format_args!("{:e}", _one_tenth_1_f), "1e-1"); - // assert_fmt_eq!(format_args!("{:#e}", _one_tenth_1_f), "1e-1"); - // assert_fmt_eq!(format_args!("{:e}", _1000_f), "1e3"); - // assert_fmt_eq!(format_args!("{:#e}", _1000_f), "1e3"); - // assert_fmt_eq!(format_args!("{:e}", _1_big_f), "1e0/3.14159e38"); - // assert_fmt_eq!(format_args!("{:#e}", _1_big_f), "1e0/3.14159e38"); - - // assert_fmt_eq!(format_args!("{:E}", _one_tenth_1_f), "1E-1"); - // assert_fmt_eq!(format_args!("{:#E}", _one_tenth_1_f), "1E-1"); - // assert_fmt_eq!(format_args!("{:E}", _1000_f), "1E3"); - // assert_fmt_eq!(format_args!("{:#E}", _1000_f), "1E3"); - // assert_fmt_eq!(format_args!("{:E}", _1_big_f), "1E0/3.14159E38"); - // assert_fmt_eq!(format_args!("{:#E}", _1_big_f), "1E0/3.14159E38"); + #[cfg(has_int_exp_fmt)] + { + assert_fmt_eq!(format_args!("{:e}", -_2), "-2e0"); + assert_fmt_eq!(format_args!("{:#e}", -_2), "-2e0"); + assert_fmt_eq!(format_args!("{:+e}", -_2), "-2e0"); + assert_fmt_eq!(format_args!("{:e}", _BILLION), "1e9"); + assert_fmt_eq!(format_args!("{:+e}", _BILLION), "+1e9"); + assert_fmt_eq!(format_args!("{:e}", _BILLION.recip()), "1e0/1e9"); + assert_fmt_eq!(format_args!("{:+e}", _BILLION.recip()), "+1e0/1e9"); + + assert_fmt_eq!(format_args!("{:E}", -_2), "-2E0"); + assert_fmt_eq!(format_args!("{:#E}", -_2), "-2E0"); + assert_fmt_eq!(format_args!("{:+E}", -_2), "-2E0"); + assert_fmt_eq!(format_args!("{:E}", _BILLION), "1E9"); + assert_fmt_eq!(format_args!("{:+E}", _BILLION), "+1E9"); + assert_fmt_eq!(format_args!("{:E}", _BILLION.recip()), "1E0/1E9"); + assert_fmt_eq!(format_args!("{:+E}", _BILLION.recip()), "+1E0/1E9"); + } } mod arith { From e0e3cb9a920588493b393e5182922ea45be539b1 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Wed, 18 Mar 2020 12:09:50 -0700 Subject: [PATCH 25/26] format build.rs --- build.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/build.rs b/build.rs index b767337..eddb6d0 100644 --- a/build.rs +++ b/build.rs @@ -1,20 +1,21 @@ fn main() { let ac = autocfg::new(); if ac.probe_expression("format!(\"{:e}\", 0_i32)") { - if !(ac.probe_expression("format!(\"{:e}\", 0_i8)") && - ac.probe_expression("format!(\"{:e}\", 0_i16)") && - ac.probe_expression("format!(\"{:e}\", 0_i32)") && - ac.probe_expression("format!(\"{:e}\", 0_i64)") && - ac.probe_expression("format!(\"{:e}\", 0_i128)") && - ac.probe_expression("format!(\"{:e}\", 0_u8)") && - ac.probe_expression("format!(\"{:e}\", 0_u16)") && - ac.probe_expression("format!(\"{:e}\", 0_u32)") && - ac.probe_expression("format!(\"{:e}\", 0_u64)") && - ac.probe_expression("format!(\"{:e}\", 0_u128)")) { + if !(ac.probe_expression("format!(\"{:e}\", 0_i8)") + && ac.probe_expression("format!(\"{:e}\", 0_i16)") + && ac.probe_expression("format!(\"{:e}\", 0_i32)") + && ac.probe_expression("format!(\"{:e}\", 0_i64)") + && ac.probe_expression("format!(\"{:e}\", 0_i128)") + && ac.probe_expression("format!(\"{:e}\", 0_u8)") + && ac.probe_expression("format!(\"{:e}\", 0_u16)") + && ac.probe_expression("format!(\"{:e}\", 0_u32)") + && ac.probe_expression("format!(\"{:e}\", 0_u64)") + && ac.probe_expression("format!(\"{:e}\", 0_u128)")) + { panic!("Some integer types implement *Exp traits, but not others") } println!("cargo:rustc-cfg=has_int_exp_fmt"); } autocfg::rerun_path(file!()); -} \ No newline at end of file +} From 05213bd36d2f58ab30bca33ecd50a4812b839702 Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Fri, 20 Mar 2020 00:31:09 -0700 Subject: [PATCH 26/26] hasten builds by only probing isize *Exp formatting --- build.rs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/build.rs b/build.rs index eddb6d0..f829088 100644 --- a/build.rs +++ b/build.rs @@ -1,19 +1,6 @@ fn main() { let ac = autocfg::new(); - if ac.probe_expression("format!(\"{:e}\", 0_i32)") { - if !(ac.probe_expression("format!(\"{:e}\", 0_i8)") - && ac.probe_expression("format!(\"{:e}\", 0_i16)") - && ac.probe_expression("format!(\"{:e}\", 0_i32)") - && ac.probe_expression("format!(\"{:e}\", 0_i64)") - && ac.probe_expression("format!(\"{:e}\", 0_i128)") - && ac.probe_expression("format!(\"{:e}\", 0_u8)") - && ac.probe_expression("format!(\"{:e}\", 0_u16)") - && ac.probe_expression("format!(\"{:e}\", 0_u32)") - && ac.probe_expression("format!(\"{:e}\", 0_u64)") - && ac.probe_expression("format!(\"{:e}\", 0_u128)")) - { - panic!("Some integer types implement *Exp traits, but not others") - } + if ac.probe_expression("format!(\"{:e}\", 0_isize)") { println!("cargo:rustc-cfg=has_int_exp_fmt"); }