diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs
index 02750d14422e4778776340fe52fc0c56f34acaa1..a4b5fa6ffa1c33943ca044de5ecd47485e3fe6c0 100644
--- a/src/header/stdlib/mod.rs
+++ b/src/header/stdlib/mod.rs
@@ -675,61 +675,12 @@ pub extern "C" fn srandom(seed: c_uint) {
 }
 
 #[no_mangle]
-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);
-    }
-
-    let mut result = 0.0;
-    let mut radix = 10;
-
-    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 unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double {
+    strto_float_impl!(c_double, s, endptr)
+}
+#[no_mangle]
+pub unsafe extern "C" fn strtof(s: *const c_char, endptr: *mut *mut c_char) -> c_float {
+    strto_float_impl!(c_float, s, endptr)
 }
 
 pub fn is_positive(ch: c_char) -> Option<(bool, isize)> {
diff --git a/src/macros.rs b/src/macros.rs
index 6a3b790e4085b88022fdf20ba8ee3d5eb879c2df..31e415c3c3cacc5cd542153a0fa5c2ec39b8a93a 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -190,3 +190,65 @@ 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;
+
+        while ctype::isspace(*s as c_int) != 0 {
+            s = s.offset(1);
+        }
+
+        let mut result: $type = 0.0;
+        let mut radix = 10;
+
+        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 $type;
+            result += digit as $type;
+            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 $type;
+                result += digit as $type / 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
+        }
+    }}
+}