From f5e8609163860d28329db6838afec87af0f220cf Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 7 Aug 2025 14:22:50 +0800 Subject: [PATCH] uucore: num_format: Move common scientific formatting code to a function Both format_float_scientific and format_float_shortest carry the same code, moving it to a single function will make it possible to optimize both. --- .../src/lib/features/format/num_format.rs | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/uucore/src/lib/features/format/num_format.rs b/src/uucore/src/lib/features/format/num_format.rs index cd38b3728..ed7eaea61 100644 --- a/src/uucore/src/lib/features/format/num_format.rs +++ b/src/uucore/src/lib/features/format/num_format.rs @@ -374,6 +374,27 @@ fn format_float_decimal( format!("{bd:.precision$}") } +/// Converts a `&BigDecimal` to a scientific-like `X.XX * 10^e`. +/// - The returned `String` contains the digits `XXX`, _without_ the separating +/// `.` (the caller must add that to get a valid scientific format number). +/// - `e` is an integer exponent. +fn bd_to_string_exp_with_prec(bd: &BigDecimal, precision: u64) -> (String, i64) { + // Round bd to precision digits (including the leading digit) + // We call `with_prec` twice as it will produce an extra digit if rounding overflows + // (e.g. 9995.with_prec(3) => 1000 * 10^1, but we want 100 * 10^2). + let bd_round = bd.with_prec(precision).with_prec(precision); + + // Convert to the form XXX * 10^-p (XXX is precision digit long) + let (frac, p) = bd_round.as_bigint_and_exponent(); + + let digits = frac.to_str_radix(10); + // If we end up with scientific formatting, we would convert XXX to X.XX: + // that divides by 10^(precision-1), so add that to the exponent. + let exponent = -p + precision as i64 - 1; + + (digits, exponent) +} + fn format_float_scientific( bd: &BigDecimal, precision: Option, @@ -395,20 +416,8 @@ fn format_float_scientific( }; } - // Round bd to (1 + precision) digits (including the leading digit) - // We call `with_prec` twice as it will produce an extra digit if rounding overflows - // (e.g. 9995.with_prec(3) => 1000 * 10^1, but we want 100 * 10^2). - let bd_round = bd - .with_prec(precision as u64 + 1) - .with_prec(precision as u64 + 1); - - // Convert to the form XXX * 10^-e (XXX is 1+precision digit long) - let (frac, e) = bd_round.as_bigint_and_exponent(); - - // Scale down "XXX" to "X.XX": that divides by 10^precision, so add that to the exponent. - let digits = frac.to_str_radix(10); + let (digits, exponent) = bd_to_string_exp_with_prec(bd, precision as u64 + 1); let (first_digit, remaining_digits) = digits.split_at(1); - let exponent = -e + precision as i64; let dot = if !remaining_digits.is_empty() || (precision == 0 && ForceDecimal::Yes == force_decimal) { @@ -445,18 +454,7 @@ fn format_float_shortest( }; } - // Round bd to precision digits (including the leading digit) - // We call `with_prec` twice as it will produce an extra digit if rounding overflows - // (e.g. 9995.with_prec(3) => 1000 * 10^1, but we want 100 * 10^2). - let bd_round = bd.with_prec(precision as u64).with_prec(precision as u64); - - // Convert to the form XXX * 10^-p (XXX is precision digit long) - let (frac, e) = bd_round.as_bigint_and_exponent(); - - let digits = frac.to_str_radix(10); - // If we end up with scientific formatting, we would convert XXX to X.XX: - // that divides by 10^(precision-1), so add that to the exponent. - let exponent = -e + precision as i64 - 1; + let (digits, exponent) = bd_to_string_exp_with_prec(bd, precision as u64); if exponent < -4 || exponent >= precision as i64 { // Scientific-ish notation (with a few differences)