From b10fa984f36786466476bc325c310f21b8c662d1 Mon Sep 17 00:00:00 2001 From: jD91mZM2 <me@krake.one> Date: Thu, 9 Aug 2018 16:34:28 +0200 Subject: [PATCH] Implement strtod --- src/stdlib/src/lib.rs | 54 ++++++++++++++++++++++++----- tests/Makefile | 1 + tests/expected/stdio/all.stdout | 1 + tests/expected/stdio/setvbuf.stderr | 0 tests/expected/stdio/setvbuf.stdout | 6 ++++ tests/expected/stdlib/strtod.stderr | 0 tests/expected/stdlib/strtod.stdout | 9 +++++ tests/stdlib/strtod.c | 17 +++++++++ 8 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 tests/expected/stdio/setvbuf.stderr create mode 100644 tests/expected/stdio/setvbuf.stdout create mode 100644 tests/expected/stdlib/strtod.stderr create mode 100644 tests/expected/stdlib/strtod.stdout create mode 100644 tests/stdlib/strtod.c diff --git a/src/stdlib/src/lib.rs b/src/stdlib/src/lib.rs index d60ffc8a..9a9cfe8e 100644 --- a/src/stdlib/src/lib.rs +++ b/src/stdlib/src/lib.rs @@ -635,19 +635,55 @@ pub extern "C" fn srandom(seed: c_uint) { } #[no_mangle] -pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double { - // TODO: endptr +pub unsafe extern "C" fn strtod(mut s: *const c_char, endptr: *mut *mut c_char) -> c_double { + while ctype::isspace(*s as c_int) != 0 { + s = s.offset(1); + } - use core::str::FromStr; + let mut result = 0.0; + let mut radix = 10; - let s_str = str::from_utf8_unchecked(platform::c_str(s)); - match f64::from_str(s_str) { - Ok(ok) => ok as c_double, - Err(_err) => { - platform::errno = EINVAL; - 0.0 + let negative = match *s as u8 { + b'-' => { s = s.offset(1); true }, + b'+' => { s = s.offset(1); false }, + _ => false + }; + + if *s as u8 == b'0' && *s.offset(1) as u8 == b'x' { + s = s.offset(2); + radix = 16; + } + + while let Some(digit) = (*s as u8 as char).to_digit(radix) { + result *= radix as c_double; + result += digit as c_double; + s = s.offset(1); + } + + 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 c_double; + result += digit as c_double / i; + s = s.offset(1); } } + + if !endptr.is_null() { + // This is stupid, but apparently strto* functions want + // const input but mut output, yet the man page says + // "stores the address of the first invalid character in *endptr" + // so obviously it doesn't want us to clone it. + *endptr = s as *mut _; + } + + if negative { + -result + } else { + result + } } pub fn is_positive(ch: c_char) -> Option<(bool, isize)> { diff --git a/tests/Makefile b/tests/Makefile index b6eacd63..33fd0564 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -28,6 +28,7 @@ EXPECT_BINS=\ stdlib/env \ stdlib/mkostemps \ stdlib/rand \ + stdlib/strtod \ stdlib/strtol \ stdlib/strtoul \ stdlib/system \ diff --git a/tests/expected/stdio/all.stdout b/tests/expected/stdio/all.stdout index 23bfb625..ebda98d0 100644 --- a/tests/expected/stdio/all.stdout +++ b/tests/expected/stdio/all.stdout @@ -1,3 +1,4 @@ H Hello World! +Hello diff --git a/tests/expected/stdio/setvbuf.stderr b/tests/expected/stdio/setvbuf.stderr new file mode 100644 index 00000000..e69de29b diff --git a/tests/expected/stdio/setvbuf.stdout b/tests/expected/stdio/setvbuf.stdout new file mode 100644 index 00000000..7f04b787 --- /dev/null +++ b/tests/expected/stdio/setvbuf.stdout @@ -0,0 +1,6 @@ +H +ello World! + +Line 2 + +Hello diff --git a/tests/expected/stdlib/strtod.stderr b/tests/expected/stdlib/strtod.stderr new file mode 100644 index 00000000..e69de29b diff --git a/tests/expected/stdlib/strtod.stdout b/tests/expected/stdlib/strtod.stdout new file mode 100644 index 00000000..c3013b85 --- /dev/null +++ b/tests/expected/stdlib/strtod.stdout @@ -0,0 +1,9 @@ +d: 0 Endptr: "a 1 hello" +d: 1 Endptr: " hello" +d: 1 Endptr: " hello 2" +d: 10.123 Endptr: "" +d: 10.123 Endptr: "" +d: -5.3 Endptr: "" +d: 16.071044921875 Endptr: "" +d: 1.13671875 Endptr: "" +d: 3.12890625 Endptr: "" diff --git a/tests/stdlib/strtod.c b/tests/stdlib/strtod.c new file mode 100644 index 00000000..21a72805 --- /dev/null +++ b/tests/stdlib/strtod.c @@ -0,0 +1,17 @@ +#include <stdlib.h> +#include <stdio.h> + +int main() { + char* endptr = 0; + double d; + + char* inputs[] = { + "a 1 hello", " 1 hello", "1 hello 2", + "10.123", "010.123", "-5.3", + "0x10.123", "0x1.23", "0x3.21" + }; + for (int i = 0; i < sizeof(inputs) / sizeof(char*); i += 1) { + d = strtod(inputs[i], &endptr); + printf("d: %f Endptr: \"%s\"\n", d, endptr); + } +} -- GitLab