Merge branch 'printf-inf-nan' into 'master'
Handle infinity and NaN in printf See merge request redox-os/relibc!252
This commit is contained in:
+81
-26
@@ -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 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,
|
||||
)?;
|
||||
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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user