diff --git a/.gitignore b/.gitignore index 94c1b10..56c7346 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ shroom_server shrooms.db auth_secret __pycache__/ + +test_float_parse diff --git a/test_float_parse.cc b/test_float_parse.cc new file mode 100644 index 0000000..c7f7e95 --- /dev/null +++ b/test_float_parse.cc @@ -0,0 +1,133 @@ +#include +#include + +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 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; +}