Ensure the floating point parsing at least kind of works

This commit is contained in:
Kelvin Ly 2024-08-25 13:45:08 -04:00
parent 19483a0f68
commit 5dd60e87e4
2 changed files with 135 additions and 0 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@ shroom_server
shrooms.db
auth_secret
__pycache__/
test_float_parse

133
test_float_parse.cc Normal file
View File

@ -0,0 +1,133 @@
#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;
}