strtof(), strtod(): handle NaN and Infinity
This commit is contained in:
committed by
Jeremy Soller
parent
fe02a5bd98
commit
b66df46f33
+74
-61
@@ -227,19 +227,19 @@ macro_rules! strto_impl {
|
||||
num
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! strto_float_impl {
|
||||
($type:ident, $s:expr, $endptr:expr) => {{
|
||||
let mut s = $s;
|
||||
let endptr = $endptr;
|
||||
|
||||
// TODO: Handle named floats: NaN, Inf...
|
||||
|
||||
while ctype::isspace(*s as c_int) != 0 {
|
||||
s = s.offset(1);
|
||||
}
|
||||
|
||||
let mut result: $type = 0.0;
|
||||
let mut exponent: Option<$type> = None;
|
||||
let mut radix = 10;
|
||||
|
||||
let result_sign = match *s as u8 {
|
||||
@@ -254,75 +254,88 @@ macro_rules! strto_float_impl {
|
||||
_ => 1.0,
|
||||
};
|
||||
|
||||
if *s as u8 == b'0' && *s.offset(1) as u8 == b'x' {
|
||||
s = s.offset(2);
|
||||
radix = 16;
|
||||
}
|
||||
let rust_s = CStr::from_ptr(s).to_string_lossy();
|
||||
|
||||
while let Some(digit) = (*s as u8 as char).to_digit(radix) {
|
||||
result *= radix as $type;
|
||||
result += digit as $type;
|
||||
s = s.offset(1);
|
||||
}
|
||||
// detect NaN, Inf
|
||||
if rust_s.to_lowercase().starts_with("inf") {
|
||||
result = $type::INFINITY;
|
||||
s = s.offset(3);
|
||||
} else if rust_s.to_lowercase().starts_with("nan") {
|
||||
// we cannot signal negative NaN in LLVM backed languages
|
||||
// https://github.com/rust-lang/rust/issues/73328 , https://github.com/rust-lang/rust/issues/81261
|
||||
result = $type::NAN;
|
||||
s = s.offset(3);
|
||||
} else {
|
||||
if *s as u8 == b'0' && *s.offset(1) as u8 == b'x' {
|
||||
s = s.offset(2);
|
||||
radix = 16;
|
||||
}
|
||||
|
||||
if *s as u8 == b'.' {
|
||||
s = s.offset(1);
|
||||
|
||||
let mut i = 1.0;
|
||||
while let Some(digit) = (*s as u8 as char).to_digit(radix) {
|
||||
i *= radix as $type;
|
||||
result += digit as $type / i;
|
||||
result *= radix as $type;
|
||||
result += digit as $type;
|
||||
s = s.offset(1);
|
||||
}
|
||||
}
|
||||
|
||||
let s_before_exponent = s;
|
||||
|
||||
let exponent = match (*s as u8, radix) {
|
||||
(b'e' | b'E', 10) | (b'p' | b'P', 16) => {
|
||||
if *s as u8 == b'.' {
|
||||
s = s.offset(1);
|
||||
|
||||
let is_exponent_positive = match *s as u8 {
|
||||
b'-' => {
|
||||
s = s.offset(1);
|
||||
false
|
||||
}
|
||||
b'+' => {
|
||||
s = s.offset(1);
|
||||
true
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
|
||||
// Exponent digits are always in base 10.
|
||||
if (*s as u8 as char).is_digit(10) {
|
||||
let mut exponent_value = 0;
|
||||
|
||||
while let Some(digit) = (*s as u8 as char).to_digit(10) {
|
||||
exponent_value *= 10;
|
||||
exponent_value += digit;
|
||||
s = s.offset(1);
|
||||
}
|
||||
|
||||
let exponent_base = match radix {
|
||||
10 => 10u128,
|
||||
16 => 2u128,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if is_exponent_positive {
|
||||
Some(exponent_base.pow(exponent_value) as $type)
|
||||
} else {
|
||||
Some(1.0 / (exponent_base.pow(exponent_value) as $type))
|
||||
}
|
||||
} else {
|
||||
// Exponent had no valid digits after 'e'/'p' and '+'/'-', rollback
|
||||
s = s_before_exponent;
|
||||
None
|
||||
let mut i = 1.0;
|
||||
while let Some(digit) = (*s as u8 as char).to_digit(radix) {
|
||||
i *= radix as $type;
|
||||
result += digit as $type / i;
|
||||
s = s.offset(1);
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let s_before_exponent = s;
|
||||
|
||||
exponent = match (*s as u8, radix) {
|
||||
(b'e' | b'E', 10) | (b'p' | b'P', 16) => {
|
||||
s = s.offset(1);
|
||||
|
||||
let is_exponent_positive = match *s as u8 {
|
||||
b'-' => {
|
||||
s = s.offset(1);
|
||||
false
|
||||
}
|
||||
b'+' => {
|
||||
s = s.offset(1);
|
||||
true
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
|
||||
// Exponent digits are always in base 10.
|
||||
if (*s as u8 as char).is_digit(10) {
|
||||
let mut exponent_value = 0;
|
||||
|
||||
while let Some(digit) = (*s as u8 as char).to_digit(10) {
|
||||
exponent_value *= 10;
|
||||
exponent_value += digit;
|
||||
s = s.offset(1);
|
||||
}
|
||||
|
||||
let exponent_base = match radix {
|
||||
10 => 10u128,
|
||||
16 => 2u128,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if is_exponent_positive {
|
||||
Some(exponent_base.pow(exponent_value) as $type)
|
||||
} else {
|
||||
Some(1.0 / (exponent_base.pow(exponent_value) as $type))
|
||||
}
|
||||
} else {
|
||||
// Exponent had no valid digits after 'e'/'p' and '+'/'-', rollback
|
||||
s = s_before_exponent;
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if !endptr.is_null() {
|
||||
// This is stupid, but apparently strto* functions want
|
||||
|
||||
Reference in New Issue
Block a user