瀏覽代碼

Implement strtod

jD91mZM2 6 年之前
父節點
當前提交
b10fa984f3

+ 45 - 9
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)> {

+ 1 - 0
tests/Makefile

@@ -28,6 +28,7 @@ EXPECT_BINS=\
 	stdlib/env \
 	stdlib/mkostemps \
 	stdlib/rand \
+	stdlib/strtod \
 	stdlib/strtol \
 	stdlib/strtoul \
 	stdlib/system \

+ 1 - 0
tests/expected/stdio/all.stdout

@@ -1,3 +1,4 @@
 H
 Hello World!
 
+Hello

+ 0 - 0
tests/expected/stdio/setvbuf.stderr


+ 6 - 0
tests/expected/stdio/setvbuf.stdout

@@ -0,0 +1,6 @@
+H
+ello World!
+
+Line 2
+
+Hello

+ 0 - 0
tests/expected/stdlib/strtod.stderr


+ 9 - 0
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: ""

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