diff --git a/src/header/stdio/printf.rs b/src/header/stdio/printf.rs index d5057d3934..33cf464ad9 100644 --- a/src/header/stdio/printf.rs +++ b/src/header/stdio/printf.rs @@ -444,9 +444,23 @@ fn pad( Ok(()) } -fn float_string(float: c_double, precision: usize, trim: bool) -> String { +fn float_string(float: c_double, precision: usize, trim: bool, alternate: bool) -> String { + // The Rust format! macro doesn't keep the dot on precision = 0 and alternate = true, + // so we have to perform a fix-up + // + // POSIX.1-2024 says "... if the precision is zero and no '#' flag is present, + // no radix character shall appear." + // + // This case is covered here. let mut string = format!("{:.p$}", float, p = precision); - if trim && string.contains('.') { + // + // Additionally, it says "For a, A, e, E, f, F, g, and G conversion specifiers, + // the result shall always contain a radix character, even if no digits follow + // the radix character." + // + if alternate && precision == 0 { + string.push('.'); + } else if trim && string.contains('.') { let truncate = { let slice = string.trim_end_matches('0'); let mut truncate = slice.len(); @@ -477,6 +491,7 @@ fn fmt_float_exp( w: &mut W, exp_fmt: char, trim: bool, + alternate: bool, precision: usize, float: c_double, exp: isize, @@ -491,7 +506,7 @@ fn fmt_float_exp( exp_len += 1; } - let string = float_string(float, precision, trim); + let string = float_string(float, precision, trim, alternate); let len = string.len() + 2 + 2.max(exp_len); pad(w, !left, b' ', len..pad_space)?; @@ -512,13 +527,14 @@ fn fmt_float_exp( fn fmt_float_normal( w: &mut W, trim: bool, + alternate: bool, precision: usize, float: c_double, left: bool, pad_space: usize, pad_zero: usize, ) -> io::Result { - let string = float_string(float, precision, trim); + let string = float_string(float, precision, trim, alternate); pad(w, !left, b' ', string.len()..pad_space)?; let bytes = if string.starts_with('-') { @@ -958,7 +974,7 @@ pub(crate) unsafe fn inner_printf( let precision = precision.unwrap_or(6); fmt_float_exp( - w, fmt, false, precision, float, exp, left, pad_space, pad_zero, + w, fmt, false, alternate, precision, float, exp, left, pad_space, pad_zero, )?; } else { fmt_float_nonfinite(w, float, fmtcase.unwrap(), left, pad_space, pad_zero)?; @@ -975,7 +991,9 @@ pub(crate) unsafe fn inner_printf( if float.is_finite() { let precision = precision.unwrap_or(6); - fmt_float_normal(w, false, precision, float, left, pad_space, pad_zero)?; + fmt_float_normal( + w, false, alternate, precision, float, left, pad_space, pad_zero, + )?; } else { fmt_float_nonfinite(w, float, fmtcase.unwrap(), left, pad_space, pad_zero)?; } @@ -1000,7 +1018,8 @@ pub(crate) unsafe fn inner_printf( // because that's how x/floor(log10(x)) works let precision = precision.saturating_sub(1); fmt_float_exp( - w, exp_fmt, !alternate, precision, log, exp, left, pad_space, pad_zero, + w, exp_fmt, !alternate, alternate, precision, log, exp, left, + pad_space, pad_zero, )?; } else { // Length of integral part will be the exponent of @@ -1010,7 +1029,7 @@ pub(crate) unsafe fn inner_printf( let len = 1 + cmp::max(0, exp) as usize; let precision = precision.saturating_sub(len); fmt_float_normal( - w, !alternate, precision, float, left, pad_space, pad_zero, + w, !alternate, alternate, precision, float, left, pad_space, pad_zero, )?; } } else {