axlibc/
strtod.rs

1use core::ffi::{c_char, c_double, c_float, c_int};
2
3macro_rules! strto_float_impl {
4    ($type:ident, $s:expr, $endptr:expr) => {{
5        let mut s = $s;
6        let endptr = $endptr;
7
8        // TODO: Handle named floats: NaN, Inf...
9
10        while isspace(*s as c_int) {
11            s = s.offset(1);
12        }
13
14        let mut result: $type = 0.0;
15        let mut radix = 10;
16
17        let result_sign = match *s as u8 {
18            b'-' => {
19                s = s.offset(1);
20                -1.0
21            }
22            b'+' => {
23                s = s.offset(1);
24                1.0
25            }
26            _ => 1.0,
27        };
28
29        if *s as u8 == b'0' && *s.offset(1) as u8 == b'x' {
30            s = s.offset(2);
31            radix = 16;
32        }
33
34        while let Some(digit) = (*s as u8 as char).to_digit(radix) {
35            result *= radix as $type;
36            result += digit as $type;
37            s = s.offset(1);
38        }
39
40        if *s as u8 == b'.' {
41            s = s.offset(1);
42
43            let mut i = 1.0;
44            while let Some(digit) = (*s as u8 as char).to_digit(radix) {
45                i *= radix as $type;
46                result += digit as $type / i;
47                s = s.offset(1);
48            }
49        }
50
51        let s_before_exponent = s;
52
53        let exponent = match (*s as u8, radix) {
54            (b'e' | b'E', 10) | (b'p' | b'P', 16) => {
55                s = s.offset(1);
56
57                let is_exponent_positive = match *s as u8 {
58                    b'-' => {
59                        s = s.offset(1);
60                        false
61                    }
62                    b'+' => {
63                        s = s.offset(1);
64                        true
65                    }
66                    _ => true,
67                };
68
69                // Exponent digits are always in base 10.
70                if (*s as u8 as char).is_digit(10) {
71                    let mut exponent_value = 0;
72
73                    while let Some(digit) = (*s as u8 as char).to_digit(10) {
74                        exponent_value *= 10;
75                        exponent_value += digit;
76                        s = s.offset(1);
77                    }
78
79                    let exponent_base = match radix {
80                        10 => 10u128,
81                        16 => 2u128,
82                        _ => unreachable!(),
83                    };
84
85                    if is_exponent_positive {
86                        Some(exponent_base.pow(exponent_value) as $type)
87                    } else {
88                        Some(1.0 / (exponent_base.pow(exponent_value) as $type))
89                    }
90                } else {
91                    // Exponent had no valid digits after 'e'/'p' and '+'/'-', rollback
92                    s = s_before_exponent;
93                    None
94                }
95            }
96            _ => None,
97        };
98
99        // Return pointer should be *mut
100        if !endptr.is_null() {
101            *endptr = s as *mut _;
102        }
103
104        if let Some(exponent) = exponent {
105            result_sign * result * exponent
106        } else {
107            result_sign * result
108        }
109    }};
110}
111
112fn isspace(c: c_int) -> bool {
113    c == c_int::from(b' ')
114        || c == c_int::from(b'\t')
115        || c == c_int::from(b'\n')
116        || c == c_int::from(b'\r')
117        || c == 0x0b
118        || c == 0x0c
119}
120
121/// Convert a string to a double-precision number.
122#[unsafe(no_mangle)]
123pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double {
124    strto_float_impl!(c_double, s, endptr)
125}
126
127/// Convert a string to a float number.
128#[unsafe(no_mangle)]
129pub unsafe extern "C" fn strtof(s: *const c_char, endptr: *mut *mut c_char) -> c_float {
130    strto_float_impl!(c_float, s, endptr)
131}