Skip to content
Snippets Groups Projects
Commit 74555698 authored by Peter Limkilde Svendsen's avatar Peter Limkilde Svendsen Committed by Jeremy Soller
Browse files

Handle infinity and NaN in printf

parent 8ba70792
No related branches found
No related tags found
No related merge requests found
......@@ -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 => {
......
......@@ -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
#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");
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment