Skip to content

Commit

Permalink
Merge pull request #7365 from drinkcat/printf-float-hex-exponent
Browse files Browse the repository at this point in the history
printf: (partially) fix hex format: exponent is decimal, correctly print negative numbers
  • Loading branch information
RenjiSann authored Feb 28, 2025
2 parents 854000e + f23e6f1 commit 5c29054
Showing 1 changed file with 35 additions and 7 deletions.
42 changes: 35 additions & 7 deletions src/uucore/src/lib/features/format/num_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,20 +465,21 @@ fn format_float_hexadecimal(
case: Case,
force_decimal: ForceDecimal,
) -> String {
let (first_digit, mantissa, exponent) = if f == 0.0 {
(0, 0, 0)
let (sign, first_digit, mantissa, exponent) = if f == 0.0 {
("", 0, 0, 0)
} else {
let bits = f.to_bits();
let exponent_bits = ((bits >> 52) & 0x7fff) as i64;
let sign = if (bits >> 63) == 1 { "-" } else { "" };
let exponent_bits = ((bits >> 52) & 0x7ff) as i64;
let exponent = exponent_bits - 1023;
let mantissa = bits & 0xf_ffff_ffff_ffff;
(1, mantissa, exponent)
(sign, 1, mantissa, exponent)
};

let mut s = match (precision, force_decimal) {
(0, ForceDecimal::No) => format!("0x{first_digit}p{exponent:+x}"),
(0, ForceDecimal::Yes) => format!("0x{first_digit}.p{exponent:+x}"),
_ => format!("0x{first_digit}.{mantissa:0>13x}p{exponent:+x}"),
(0, ForceDecimal::No) => format!("{sign}0x{first_digit}p{exponent:+}"),
(0, ForceDecimal::Yes) => format!("{sign}0x{first_digit}.p{exponent:+}"),
_ => format!("{sign}0x{first_digit}.{mantissa:0>13x}p{exponent:+}"),
};

if case == Case::Uppercase {
Expand Down Expand Up @@ -654,6 +655,33 @@ mod test {
assert_eq!(f(99_999_999.0), "1.e+08");
}

#[test]
fn hexadecimal_float() {
use super::format_float_hexadecimal;
let f = |x| format_float_hexadecimal(x, 6, Case::Lowercase, ForceDecimal::No);
// TODO(#7364): These values do not match coreutils output, but are possible correct representations.
assert_eq!(f(0.00001), "0x1.4f8b588e368f1p-17");
assert_eq!(f(0.125), "0x1.0000000000000p-3");
assert_eq!(f(256.0), "0x1.0000000000000p+8");
assert_eq!(f(65536.0), "0x1.0000000000000p+16");
assert_eq!(f(-0.00001), "-0x1.4f8b588e368f1p-17");
assert_eq!(f(-0.125), "-0x1.0000000000000p-3");
assert_eq!(f(-256.0), "-0x1.0000000000000p+8");
assert_eq!(f(-65536.0), "-0x1.0000000000000p+16");

let f = |x| format_float_hexadecimal(x, 0, Case::Lowercase, ForceDecimal::No);
assert_eq!(f(0.125), "0x1p-3");
assert_eq!(f(256.0), "0x1p+8");
assert_eq!(f(-0.125), "-0x1p-3");
assert_eq!(f(-256.0), "-0x1p+8");

let f = |x| format_float_hexadecimal(x, 0, Case::Lowercase, ForceDecimal::Yes);
assert_eq!(f(0.125), "0x1.p-3");
assert_eq!(f(256.0), "0x1.p+8");
assert_eq!(f(-0.125), "-0x1.p-3");
assert_eq!(f(-256.0), "-0x1.p+8");
}

#[test]
fn strip_insignificant_end() {
use super::strip_fractional_zeroes_and_dot;
Expand Down

0 comments on commit 5c29054

Please sign in to comment.