Skip to content

Commit 09d7b2d

Browse files
authored
Merge pull request #7458 from drinkcat/format-bigdecimal
Move ExtendedBigDecimal to uucore/format, make use of it in formatting functions
2 parents b4a9b89 + d678e53 commit 09d7b2d

File tree

14 files changed

+553
-261
lines changed

14 files changed

+553
-261
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/csplit/src/split_name.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::csplit_error::CsplitError;
1212
/// format.
1313
pub struct SplitName {
1414
prefix: Vec<u8>,
15-
format: Format<UnsignedInt>,
15+
format: Format<UnsignedInt, u64>,
1616
}
1717

1818
impl SplitName {
@@ -52,7 +52,7 @@ impl SplitName {
5252
None => format!("%0{n_digits}u"),
5353
};
5454

55-
let format = match Format::<UnsignedInt>::parse(format_string) {
55+
let format = match Format::<UnsignedInt, u64>::parse(format_string) {
5656
Ok(format) => Ok(format),
5757
Err(FormatError::TooManySpecs(_)) => Err(CsplitError::SuffixFormatTooManyPercents),
5858
Err(_) => Err(CsplitError::SuffixFormatIncorrect),

src/uu/dd/src/progress.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ impl ProgUpdate {
157157
variant: FloatVariant::Shortest,
158158
..Default::default()
159159
}
160-
.fmt(&mut duration_str, duration)?;
160+
.fmt(&mut duration_str, &duration.into())?;
161161
// We assume that printf will output valid UTF-8
162162
let duration_str = std::str::from_utf8(&duration_str).unwrap();
163163

src/uu/seq/BENCHMARKING.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ hyperfine -L seq seq,./target/release/seq "{seq} 0 0.000001 1"
4343
hyperfine -L seq seq,./target/release/seq "{seq} -100 1 1000000"
4444
```
4545

46+
It is also interesting to compare performance with large precision
47+
format. But in this case, the output itself should also be compared,
48+
as GNU `seq` may not provide the same precision (`uutils` version of
49+
`seq` provides arbitrary precision, while GNU `seq` appears to be
50+
limited to `long double` on the given platform, i.e. 64/80/128-bit
51+
float):
52+
```shell
53+
hyperfine -L seq seq,target/release/seq "{seq} -f%.30f 0 0.000001 1"
54+
```
55+
4656
## Optimizations
4757

4858
### Buffering stdout

src/uu/seq/src/hexadecimalfloat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55
// spell-checker:ignore extendedbigdecimal bigdecimal hexdigit numberparse
6-
use crate::extendedbigdecimal::ExtendedBigDecimal;
76
use crate::number::PreciseNumber;
87
use crate::numberparse::ParseNumberError;
98
use bigdecimal::BigDecimal;
109
use num_traits::FromPrimitive;
10+
use uucore::format::ExtendedBigDecimal;
1111

1212
/// The base of the hex number system
1313
const HEX_RADIX: u32 = 16;

src/uu/seq/src/number.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// spell-checker:ignore extendedbigdecimal
66
use num_traits::Zero;
77

8-
use crate::extendedbigdecimal::ExtendedBigDecimal;
8+
use uucore::format::ExtendedBigDecimal;
99

1010
/// A number with a specified number of integer and fractional digits.
1111
///

src/uu/seq/src/numberparse.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ use num_bigint::Sign;
1515
use num_traits::Num;
1616
use num_traits::Zero;
1717

18-
use crate::extendedbigdecimal::ExtendedBigDecimal;
1918
use crate::hexadecimalfloat;
2019
use crate::number::PreciseNumber;
20+
use uucore::format::ExtendedBigDecimal;
2121

2222
/// An error returned when parsing a number fails.
2323
#[derive(Debug, PartialEq, Eq)]
@@ -381,8 +381,8 @@ impl FromStr for PreciseNumber {
381381
#[cfg(test)]
382382
mod tests {
383383
use bigdecimal::BigDecimal;
384+
use uucore::format::ExtendedBigDecimal;
384385

385-
use crate::extendedbigdecimal::ExtendedBigDecimal;
386386
use crate::number::PreciseNumber;
387387
use crate::numberparse::ParseNumberError;
388388

src/uu/seq/src/seq.rs

Lines changed: 45 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ use std::ffi::OsString;
77
use std::io::{stdout, BufWriter, ErrorKind, Write};
88

99
use clap::{Arg, ArgAction, Command};
10-
use num_traits::{ToPrimitive, Zero};
10+
use num_traits::Zero;
1111

1212
use uucore::error::{FromIo, UResult};
13-
use uucore::format::{num_format, sprintf, Format, FormatArgument};
13+
use uucore::format::num_format::FloatVariant;
14+
use uucore::format::{num_format, ExtendedBigDecimal, Format};
1415
use uucore::{format_usage, help_about, help_usage};
1516

1617
mod error;
17-
mod extendedbigdecimal;
1818
mod hexadecimalfloat;
1919

2020
// public to allow fuzzing
@@ -24,7 +24,6 @@ pub mod number;
2424
mod number;
2525
mod numberparse;
2626
use crate::error::SeqError;
27-
use crate::extendedbigdecimal::ExtendedBigDecimal;
2827
use crate::number::PreciseNumber;
2928

3029
const ABOUT: &str = help_about!("seq.md");
@@ -142,26 +141,52 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
142141
}
143142
};
144143

145-
let padding = first
146-
.num_integral_digits
147-
.max(increment.num_integral_digits)
148-
.max(last.num_integral_digits);
149-
150144
let precision = select_precision(first_precision, increment_precision, last_precision);
151145

152-
let format = options
153-
.format
154-
.map(Format::<num_format::Float>::parse)
155-
.transpose()?;
146+
// If a format was passed on the command line, use that.
147+
// If not, use some default format based on parameters precision.
148+
let format = match options.format {
149+
Some(str) => Format::<num_format::Float, &ExtendedBigDecimal>::parse(str)?,
150+
None => {
151+
let padding = if options.equal_width {
152+
let precision_value = precision.unwrap_or(0);
153+
first
154+
.num_integral_digits
155+
.max(increment.num_integral_digits)
156+
.max(last.num_integral_digits)
157+
+ if precision_value > 0 {
158+
precision_value + 1
159+
} else {
160+
0
161+
}
162+
} else {
163+
0
164+
};
165+
166+
let formatter = match precision {
167+
// format with precision: decimal floats and integers
168+
Some(precision) => num_format::Float {
169+
variant: FloatVariant::Decimal,
170+
width: padding,
171+
alignment: num_format::NumberAlignment::RightZero,
172+
precision,
173+
..Default::default()
174+
},
175+
// format without precision: hexadecimal floats
176+
None => num_format::Float {
177+
variant: FloatVariant::Shortest,
178+
..Default::default()
179+
},
180+
};
181+
Format::from_formatter(formatter)
182+
}
183+
};
156184

157185
let result = print_seq(
158186
(first.number, increment.number, last.number),
159-
precision,
160187
&options.separator,
161188
&options.terminator,
162-
options.equal_width,
163-
padding,
164-
format.as_ref(),
189+
&format,
165190
);
166191
match result {
167192
Ok(()) => Ok(()),
@@ -220,93 +245,24 @@ fn done_printing<T: Zero + PartialOrd>(next: &T, increment: &T, last: &T) -> boo
220245
}
221246
}
222247

223-
fn format_bigdecimal(value: &bigdecimal::BigDecimal) -> Option<String> {
224-
let format_arguments = &[FormatArgument::Float(value.to_f64()?)];
225-
let value_as_bytes = sprintf("%g", format_arguments).ok()?;
226-
String::from_utf8(value_as_bytes).ok()
227-
}
228-
229-
/// Write a big decimal formatted according to the given parameters.
230-
fn write_value_float(
231-
writer: &mut impl Write,
232-
value: &ExtendedBigDecimal,
233-
width: usize,
234-
precision: Option<usize>,
235-
) -> std::io::Result<()> {
236-
let value_as_str = match precision {
237-
// format with precision: decimal floats and integers
238-
Some(precision) => match value {
239-
ExtendedBigDecimal::Infinity | ExtendedBigDecimal::MinusInfinity => {
240-
format!("{value:>width$.precision$}")
241-
}
242-
_ => format!("{value:>0width$.precision$}"),
243-
},
244-
// format without precision: hexadecimal floats
245-
None => match value {
246-
ExtendedBigDecimal::BigDecimal(bd) => {
247-
format_bigdecimal(bd).unwrap_or_else(|| "{value}".to_owned())
248-
}
249-
_ => format!("{value:>0width$}"),
250-
},
251-
};
252-
write!(writer, "{value_as_str}")
253-
}
254-
255248
/// Floating point based code path
256249
fn print_seq(
257250
range: RangeFloat,
258-
precision: Option<usize>,
259251
separator: &str,
260252
terminator: &str,
261-
pad: bool,
262-
padding: usize,
263-
format: Option<&Format<num_format::Float>>,
253+
format: &Format<num_format::Float, &ExtendedBigDecimal>,
264254
) -> std::io::Result<()> {
265255
let stdout = stdout().lock();
266256
let mut stdout = BufWriter::new(stdout);
267257
let (first, increment, last) = range;
268258
let mut value = first;
269-
let padding = if pad {
270-
let precision_value = precision.unwrap_or(0);
271-
padding
272-
+ if precision_value > 0 {
273-
precision_value + 1
274-
} else {
275-
0
276-
}
277-
} else {
278-
0
279-
};
259+
280260
let mut is_first_iteration = true;
281261
while !done_printing(&value, &increment, &last) {
282262
if !is_first_iteration {
283263
write!(stdout, "{separator}")?;
284264
}
285-
// If there was an argument `-f FORMAT`, then use that format
286-
// template instead of the default formatting strategy.
287-
//
288-
// TODO The `printf()` method takes a string as its second
289-
// parameter but we have an `ExtendedBigDecimal`. In order to
290-
// satisfy the signature of the function, we convert the
291-
// `ExtendedBigDecimal` into a string. The `printf()`
292-
// logic will subsequently parse that string into something
293-
// similar to an `ExtendedBigDecimal` again before rendering
294-
// it as a string and ultimately writing to `stdout`. We
295-
// shouldn't have to do so much converting back and forth via
296-
// strings.
297-
match &format {
298-
Some(f) => {
299-
let float = match &value {
300-
ExtendedBigDecimal::BigDecimal(bd) => bd.to_f64().unwrap(),
301-
ExtendedBigDecimal::Infinity => f64::INFINITY,
302-
ExtendedBigDecimal::MinusInfinity => f64::NEG_INFINITY,
303-
ExtendedBigDecimal::MinusZero => -0.0,
304-
ExtendedBigDecimal::Nan => f64::NAN,
305-
};
306-
f.fmt(&mut stdout, float)?;
307-
}
308-
None => write_value_float(&mut stdout, &value, padding, precision)?,
309-
}
265+
format.fmt(&mut stdout, &value)?;
310266
// TODO Implement augmenting addition.
311267
value = value + increment.clone();
312268
is_first_iteration = false;

src/uucore/Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# spell-checker:ignore (features) zerocopy
1+
# spell-checker:ignore (features) bigdecimal zerocopy
22

33
[package]
44
name = "uucore"
@@ -58,6 +58,8 @@ blake3 = { workspace = true, optional = true }
5858
sm3 = { workspace = true, optional = true }
5959
crc32fast = { workspace = true, optional = true }
6060
regex = { workspace = true, optional = true }
61+
bigdecimal = { workspace = true, optional = true }
62+
num-traits = { workspace = true, optional = true }
6163

6264
[target.'cfg(unix)'.dependencies]
6365
walkdir = { workspace = true, optional = true }
@@ -94,7 +96,7 @@ fs = ["dunce", "libc", "winapi-util", "windows-sys"]
9496
fsext = ["libc", "windows-sys"]
9597
fsxattr = ["xattr"]
9698
lines = []
97-
format = ["itertools", "quoting-style"]
99+
format = ["bigdecimal", "itertools", "num-traits", "quoting-style"]
98100
mode = ["libc"]
99101
perms = ["entries", "libc", "walkdir"]
100102
buf-copy = []

0 commit comments

Comments
 (0)