Skip to content

Commit e94793f

Browse files
committed
uucore: num_format: Reduce calls to with_prec
with_prec is actually really expensive, so it's much better to just call it once, and handle the rounding corner case manually. Saves about ~20% performance on: ``` {seq} -f "%g" 0 1e-9 1e-3 ```
1 parent f5e8609 commit e94793f

File tree

1 file changed

+22
-8
lines changed

1 file changed

+22
-8
lines changed

src/uucore/src/lib/features/format/num_format.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -378,16 +378,30 @@ fn format_float_decimal(
378378
/// - The returned `String` contains the digits `XXX`, _without_ the separating
379379
/// `.` (the caller must add that to get a valid scientific format number).
380380
/// - `e` is an integer exponent.
381-
fn bd_to_string_exp_with_prec(bd: &BigDecimal, precision: u64) -> (String, i64) {
381+
fn bd_to_string_exp_with_prec(bd: &BigDecimal, precision: usize) -> (String, i64) {
382+
// TODO: A lot of time is spent in `with_prec` computing the exact number
383+
// of digits, it might be possible to save computation time by doing a rough
384+
// division followed by arithmetics on `digits` to round if necessary (using
385+
// `fast_inc`).
386+
382387
// Round bd to precision digits (including the leading digit)
383-
// We call `with_prec` twice as it will produce an extra digit if rounding overflows
384-
// (e.g. 9995.with_prec(3) => 1000 * 10^1, but we want 100 * 10^2).
385-
let bd_round = bd.with_prec(precision).with_prec(precision);
388+
// Note that `with_prec` will produce an extra digit if rounding overflows
389+
// (e.g. 9995.with_prec(3) => 1000 * 10^1, but we want 100 * 10^2), we compensate
390+
// for that later.
391+
let bd_round = bd.with_prec(precision as u64);
386392

387393
// Convert to the form XXX * 10^-p (XXX is precision digit long)
388-
let (frac, p) = bd_round.as_bigint_and_exponent();
394+
let (frac, mut p) = bd_round.as_bigint_and_exponent();
395+
396+
let mut digits = frac.to_str_radix(10);
397+
398+
// In the unlikely case we had an overflow, correct for that.
399+
if digits.len() == precision + 1 {
400+
debug_assert!(&digits[precision..] == "0");
401+
digits.truncate(precision);
402+
p -= 1;
403+
}
389404

390-
let digits = frac.to_str_radix(10);
391405
// If we end up with scientific formatting, we would convert XXX to X.XX:
392406
// that divides by 10^(precision-1), so add that to the exponent.
393407
let exponent = -p + precision as i64 - 1;
@@ -416,7 +430,7 @@ fn format_float_scientific(
416430
};
417431
}
418432

419-
let (digits, exponent) = bd_to_string_exp_with_prec(bd, precision as u64 + 1);
433+
let (digits, exponent) = bd_to_string_exp_with_prec(bd, precision + 1);
420434
let (first_digit, remaining_digits) = digits.split_at(1);
421435

422436
let dot =
@@ -454,7 +468,7 @@ fn format_float_shortest(
454468
};
455469
}
456470

457-
let (digits, exponent) = bd_to_string_exp_with_prec(bd, precision as u64);
471+
let (digits, exponent) = bd_to_string_exp_with_prec(bd, precision);
458472

459473
if exponent < -4 || exponent >= precision as i64 {
460474
// Scientific-ish notation (with a few differences)

0 commit comments

Comments
 (0)