Prechádzať zdrojové kódy

Handle infinity and NaN in printf

Peter Limkilde Svendsen 5 rokov pred
rodič
commit
74555698fb

+ 82 - 27
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 => {

+ 8 - 0
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

+ 13 - 0
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");
+    }
 }