134 lines
3.3 KiB
C++
134 lines
3.3 KiB
C++
#include <optional>
|
|
#include <iostream>
|
|
|
|
bool parseFloat(const char* buf, int len, float* v) {
|
|
float tmp = 0.0f;
|
|
float factor = 0.1f;
|
|
int exponent = 0;
|
|
bool is_negative = false, has_digit = false;
|
|
bool using_exponent = false, has_exponent = false;
|
|
bool exp_is_negative = false;
|
|
|
|
enum State {
|
|
kPredecimal,
|
|
kPostdecimal, // after the decimal point
|
|
kExponent, // after the e
|
|
kExponentPostsign, // after the +/-
|
|
};
|
|
State state = kPredecimal;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
switch (state) {
|
|
case kPredecimal:
|
|
if (i == 0 && buf[i] == '-') {
|
|
is_negative = true;
|
|
} else if (buf[i] >= '0' && buf[i] <= '9') {
|
|
has_digit = true;
|
|
tmp = 10.0f * tmp + (buf[i] - '0');
|
|
} else if (buf[i] == '.') {
|
|
state = kPostdecimal;
|
|
} else if (buf[i] == 'e') {
|
|
state = kExponent;
|
|
using_exponent = true;
|
|
} else {
|
|
return false;
|
|
}
|
|
break;
|
|
case kPostdecimal:
|
|
if (buf[i] >= '0' && buf[i] <= '9') {
|
|
has_digit = true;
|
|
tmp = tmp + factor * (buf[i] - '0');
|
|
factor *= 0.1f;
|
|
} else if (buf[i] == 'e') {
|
|
state = kExponent;
|
|
using_exponent = true;
|
|
} else {
|
|
return false;
|
|
}
|
|
break;
|
|
case kExponent:
|
|
if (buf[i] == '+') {
|
|
state = kExponentPostsign;
|
|
} else if (buf[i] == '-') {
|
|
exp_is_negative = true;
|
|
state = kExponentPostsign;
|
|
} else if (buf[i] >= '0' && buf[i] <= '9') {
|
|
has_exponent = true;
|
|
exponent = (buf[i] - '0');
|
|
state = kExponentPostsign;
|
|
} else {
|
|
return false;
|
|
}
|
|
break;
|
|
case kExponentPostsign:
|
|
if (buf[i] >= '0' && buf[i] <= '9') {
|
|
has_exponent = true;
|
|
exponent = 10*exponent + (buf[i] - '0');
|
|
} else {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!has_digit) return false;
|
|
if (using_exponent && !has_exponent) return false;
|
|
|
|
if (is_negative) {
|
|
tmp = -tmp;
|
|
}
|
|
if (has_exponent) {
|
|
if (exp_is_negative) {
|
|
for (int i = 0; i < exponent; i++) {
|
|
tmp *= 0.1f;
|
|
}
|
|
} else {
|
|
for (int i = 0; i < exponent; i++) {
|
|
tmp *= 10.f;
|
|
}
|
|
}
|
|
}
|
|
|
|
*v = tmp;
|
|
return true;
|
|
}
|
|
|
|
std::optional<float> parseWrapper(const char* buf, int len) {
|
|
float tmp;
|
|
if (parseFloat(buf, len, &tmp)) {
|
|
return tmp;
|
|
} else {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
#define ASSERT_VALID_MATCHES(s) \
|
|
do { \
|
|
auto tmp = parseWrapper(#s, sizeof(#s) - 1); \
|
|
if (!tmp) { \
|
|
std::cout << "failed to parse " << s << std::endl; \
|
|
return -1; \
|
|
} \
|
|
float err = (*tmp - s) / (s + 1e-12); \
|
|
if (err < 0) err = -err; \
|
|
if (err > 1e-6) { \
|
|
std::cout << "error out of range for " << #s << " err = " << err << ", parsed = " << *tmp << ", expected = " << s << std::endl; \
|
|
} \
|
|
} while (0)
|
|
|
|
int main() {
|
|
ASSERT_VALID_MATCHES(1.);
|
|
ASSERT_VALID_MATCHES(0.1);
|
|
ASSERT_VALID_MATCHES(-0.1);
|
|
ASSERT_VALID_MATCHES(-0.1e3);
|
|
ASSERT_VALID_MATCHES(-0.1e-3);
|
|
ASSERT_VALID_MATCHES(1.2e-3);
|
|
ASSERT_VALID_MATCHES(.2e-3);
|
|
ASSERT_VALID_MATCHES(-.3e-4);
|
|
ASSERT_VALID_MATCHES(-.3e4);
|
|
ASSERT_VALID_MATCHES(-.0003342);
|
|
ASSERT_VALID_MATCHES(-.0073342e12);
|
|
ASSERT_VALID_MATCHES(3.141592);
|
|
return 0;
|
|
}
|