Skip to content

Commit f7f3bbc

Browse files
committed
uucore: num_format: Optimize format_float_shortest
We already know the String length ahead of time, and we can avoid using `format`. Saves about ~30% performance on: ``` {seq} -f "%g" 0 1e-9 1e-3 ```
1 parent e94793f commit f7f3bbc

File tree

1 file changed

+26
-12
lines changed

1 file changed

+26
-12
lines changed

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

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,8 @@ fn format_float_scientific(
431431
}
432432

433433
let (digits, exponent) = bd_to_string_exp_with_prec(bd, precision + 1);
434+
435+
// TODO: Optimizations in format_float_shortest can be made here as well
434436
let (first_digit, remaining_digits) = digits.split_at(1);
435437

436438
let dot =
@@ -468,6 +470,7 @@ fn format_float_shortest(
468470
};
469471
}
470472

473+
let mut output = String::with_capacity(precision);
471474
let (digits, exponent) = bd_to_string_exp_with_prec(bd, precision);
472475

473476
if exponent < -4 || exponent >= precision as i64 {
@@ -477,42 +480,53 @@ fn format_float_shortest(
477480
let (first_digit, remaining_digits) = digits.split_at(1);
478481

479482
// Always add the dot, we might trim it later.
480-
let mut normalized = format!("{first_digit}.{remaining_digits}");
483+
output.push_str(first_digit);
484+
output.push('.');
485+
output.push_str(remaining_digits);
481486

482487
if force_decimal == ForceDecimal::No {
483-
strip_fractional_zeroes_and_dot(&mut normalized);
488+
strip_fractional_zeroes_and_dot(&mut output);
484489
}
485490

486-
let exp_char = match case {
491+
output.push(match case {
487492
Case::Lowercase => 'e',
488493
Case::Uppercase => 'E',
489-
};
494+
});
490495

491-
format!("{normalized}{exp_char}{exponent:+03}")
496+
// Format the exponent
497+
let exponent_abs = exponent.abs();
498+
output.push(if exponent < 0 { '-' } else { '+' });
499+
if exponent_abs < 10 {
500+
output.push('0');
501+
}
502+
output.push_str(&exponent_abs.to_string());
492503
} else {
493504
// Decimal-ish notation with a few differences:
494505
// - The precision works differently and specifies the total number
495506
// of digits instead of the digits in the fractional part.
496507
// - If we don't force the decimal, `.` and trailing `0` in the fractional part
497508
// are trimmed.
498-
let mut formatted = if exponent < 0 {
509+
if exponent < 0 {
499510
// Small number, prepend some "0.00" string
500-
let zeros = "0".repeat(-exponent as usize - 1);
501-
format!("0.{zeros}{digits}")
511+
output.push_str("0.");
512+
output.extend(std::iter::repeat_n('0', -exponent as usize - 1));
513+
output.push_str(&digits);
502514
} else {
503515
// exponent >= 0, slot in a dot at the right spot
504516
let (first_digits, remaining_digits) = digits.split_at(exponent as usize + 1);
505517

506518
// Always add `.` even if it's trailing, we might trim it later
507-
format!("{first_digits}.{remaining_digits}")
519+
output.push_str(first_digits);
520+
output.push('.');
521+
output.push_str(remaining_digits);
508522
};
509523

510524
if force_decimal == ForceDecimal::No {
511-
strip_fractional_zeroes_and_dot(&mut formatted);
525+
strip_fractional_zeroes_and_dot(&mut output);
512526
}
513-
514-
formatted
515527
}
528+
529+
output
516530
}
517531

518532
fn format_float_hexadecimal(

0 commit comments

Comments
 (0)