diff --git a/src/header/stdio/printf.rs b/src/header/stdio/printf.rs index 5deec6de6ffa8ae27ad68d13b2b12d6103993160..d8b06bc70f3b4c4e964c9f0933a637d182c33f75 100644 --- a/src/header/stdio/printf.rs +++ b/src/header/stdio/printf.rs @@ -4,7 +4,7 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; -use core::{char, cmp, f64, ffi::VaList, fmt, ops::Range, slice}; +use core::{char, cmp, f64, ffi::VaList, fmt, num::FpCategory, ops::Range, slice}; use crate::{ header::errno::EILSEQ, @@ -259,6 +259,18 @@ impl VaListCache { // |___|_| |_| |_| .__/|_|\___|_| |_| |_|\___|_| |_|\__\__,_|\__|_|\___/|_| |_(_) // |_| +enum FmtCase { + Lower, + Upper, +} + +// The spelled-out "infinity"/"INFINITY" is also permitted by the standard +static INF_STR_LOWER: &str = "inf"; +static INF_STR_UPPER: &str = "INF"; + +static NAN_STR_LOWER: &str = "nan"; +static NAN_STR_UPPER: &str = "NAN"; + unsafe fn pop_int_raw(format: &mut *const u8) -> Option<usize> { let mut int = None; while let Some(digit) = (**format as char).to_digit(10) { @@ -420,6 +432,32 @@ fn fmt_float_normal<W: Write>( Ok(string.len()) } +/// Write ±infinity or ±NaN representation for any floating-point style +fn fmt_float_nonfinite<W: Write>(w: &mut W, float: c_double, case: FmtCase) -> io::Result<()> { + if float.is_sign_negative() { + w.write_all(&[b'-'])?; + } + + let nonfinite_str = match float.classify() { + FpCategory::Infinite => match case { + FmtCase::Lower => INF_STR_LOWER, + FmtCase::Upper => INF_STR_UPPER, + }, + FpCategory::Nan => match case { + FmtCase::Lower => NAN_STR_LOWER, + FmtCase::Upper => NAN_STR_UPPER, + }, + _ => { + // This function should only be called with infinite or NaN value. + panic!("this should not be possible") + } + }; + + w.write_all(nonfinite_str.as_bytes())?; + + Ok(()) +} + #[derive(Clone, Copy)] struct PrintfIter { format: *const u8, @@ -634,6 +672,11 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) -> let intkind = arg.intkind; let fmt = arg.fmt; let fmtkind = arg.fmtkind; + let fmtcase = match fmt { + b'x' | b'f' | b'e' | b'g' => Some(FmtCase::Lower), + b'X' | b'F' | b'E' | b'G' => Some(FmtCase::Upper), + _ => None, + }; let index = arg.index.map(|i| i - 1).unwrap_or_else(|| { if fmtkind == FmtKind::Percent { @@ -756,47 +799,59 @@ unsafe fn inner_printf<W: Write>(w: W, format: *const c_char, mut ap: VaList) -> VaArg::c_double(i) => i, _ => panic!("this should not be possible"), }; - let (float, exp) = float_exp(float); - let precision = precision.unwrap_or(6); + if float.is_finite() { + let (float, exp) = float_exp(float); + let precision = precision.unwrap_or(6); - fmt_float_exp( - w, fmt, false, precision, float, exp, left, pad_space, pad_zero, - )?; + fmt_float_exp( + w, fmt, false, precision, float, exp, left, pad_space, pad_zero, + )?; + } else { + fmt_float_nonfinite(w, float, fmtcase.unwrap())?; + } } FmtKind::Decimal => { let float = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) { VaArg::c_double(i) => i, _ => panic!("this should not be possible"), }; - let precision = precision.unwrap_or(6); + 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, precision, float, left, pad_space, pad_zero)?; + } else { + fmt_float_nonfinite(w, float, fmtcase.unwrap())?; + } } FmtKind::AnyNotation => { let float = match varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) { VaArg::c_double(i) => i, _ => panic!("this should not be possible"), }; - let (log, exp) = float_exp(float); - let exp_fmt = b'E' | (fmt & 32); - let precision = precision.unwrap_or(6); - let use_exp_format = exp < -4 || exp >= precision as isize; - - if use_exp_format { - // Length of integral part will always be 1 here, - // because that's how x/floor(log10(x)) works - let precision = precision.saturating_sub(1); - fmt_float_exp( - w, exp_fmt, true, precision, log, exp, left, pad_space, pad_zero, - )?; + if float.is_finite() { + let (log, exp) = float_exp(float); + let exp_fmt = b'E' | (fmt & 32); + let precision = precision.unwrap_or(6); + let use_exp_format = exp < -4 || exp >= precision as isize; + + if use_exp_format { + // Length of integral part will always be 1 here, + // because that's how x/floor(log10(x)) works + let precision = precision.saturating_sub(1); + fmt_float_exp( + w, exp_fmt, true, precision, log, exp, left, pad_space, pad_zero, + )?; + } else { + // Length of integral part will be the exponent of + // the unused logarithm, unless the exponent is + // negative which in case the integral part must + // of course be 0, 1 in length + let len = 1 + cmp::max(0, exp) as usize; + let precision = precision.saturating_sub(len); + fmt_float_normal(w, true, precision, float, left, pad_space, pad_zero)?; + } } else { - // Length of integral part will be the exponent of - // the unused logarithm, unless the exponent is - // negative which in case the integral part must - // of course be 0, 1 in length - let len = 1 + cmp::max(0, exp) as usize; - let precision = precision.saturating_sub(len); - fmt_float_normal(w, true, precision, float, left, pad_space, pad_zero)?; + fmt_float_nonfinite(w, float, fmtcase.unwrap())?; } } FmtKind::String => { diff --git a/tests/expected/stdio/printf.stdout b/tests/expected/stdio/printf.stdout index 00f5266f4924f2d607ed1986d3e04646a9d44f6d..ca878f7fa6d6ac669fff2fcf54fbee6c5fb0f989 100644 --- a/tests/expected/stdio/printf.stdout +++ b/tests/expected/stdio/printf.stdout @@ -52,3 +52,11 @@ Float madness: 0.0001 1E-05 1.000000E-05 + +Non-finite float madness: +%e: inf -inf nan -nan +%E: INF -INF NAN -NAN +%f: inf -inf nan -nan +%F: INF -INF NAN -NAN +%g: inf -inf nan -nan +%G: INF -INF NAN -NAN diff --git a/tests/stdio/printf.c b/tests/stdio/printf.c index f2c9f36e929a324bbd80967359ad7c032aefd13a..6e05cad21ccedbc7f6cf464366de28d058fee10d 100644 --- a/tests/stdio/printf.c +++ b/tests/stdio/printf.c @@ -1,4 +1,5 @@ #include <stdio.h> +#include <math.h> // INFINITY, NAN constants int main(void) { int sofar = 0; @@ -61,4 +62,16 @@ int main(void) { printf("%G\n", 0.0001); printf("%G\n", 0.00001); printf("%E\n", 0.00001); + + double nonfinites[] = {INFINITY, -INFINITY, NAN, -NAN}; + char *float_formats[] = {"%e", "%E", "%f", "%F", "%g", "%G"}; + puts("\nNon-finite float madness:"); + for (size_t i = 0; i < sizeof(float_formats)/sizeof(char *); i++) { + printf("%s:", float_formats[i]); + for (size_t j = 0; j < sizeof(nonfinites)/sizeof(double); j++) { + printf(" "); + printf(float_formats[i], nonfinites[j]); + } + printf("\n"); + } }