Skip to content

Commit

Permalink
Add svtod: convert TextView to double.
Browse files Browse the repository at this point in the history
  • Loading branch information
SolidWallOfCode committed May 22, 2020
1 parent c09a104 commit c47ba26
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 4 deletions.
17 changes: 17 additions & 0 deletions code/include/swoc/TextView.h
Original file line number Diff line number Diff line change
Expand Up @@ -822,11 +822,28 @@ svto_radix(swoc::TextView &src) {
return zret;
}

/// Convenience overload.
/// @see @svto_radix(swoc::TextView &src)
template <int N>
uintmax_t
svto_radix(swoc::TextView &&src) {
return svto_radix<N>(src);
}

/** Parse @a text as a floating point number.
*
* @param text The input text.
* @param parsed Parsed text [out]
* @return The floating point value, or 0.0 if invalid input.
*
* If @a parsed is not @a nullptr then the span of characters parsed is put there. This can be
* used to check if the parse was scuccesful - on a failed parse, it will be empty.
*
* @note This should be within 1 epsilon of correct, although it doesn't guarantee picking
* the closest epsilon. It's more than sufficient for use in configurations, but possibly
* not for high precision work.
*/
double svtod(swoc::TextView text, swoc::TextView * parsed = nullptr);
// ----------------------------------------------------------
// Inline implementations.
// Note: Why, you may ask, do I use @c TextView::self_type for return type instead of the
Expand Down
8 changes: 4 additions & 4 deletions code/include/swoc/swoc_ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ union IPEndpoint {

/** Break a string in to IP address relevant tokens.
*
* @param [in] src Source tex.t
* @param [out] host The host / address.
* @param [out] port The port.
* @param [out] rest Any text past the end of the IP address.
* @param src Source text. [in]
* @param host The host / address. [out]
* @param port The port. [out]
* @param rest Any text past the end of the IP address. [out]
* @return @c true if an IP address was found, @c false otherwise.
*
* Any of the out parameters can be @c nullptr in which case they are not updated.
Expand Down
80 changes: 80 additions & 0 deletions code/src/TextView.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include <cctype>
#include <sstream>

using namespace swoc::literals;

int
memcmp(std::string_view const &lhs, std::string_view const &rhs)
{
Expand Down Expand Up @@ -162,6 +164,84 @@ svtou(TextView src, TextView *out, int base) {
return zret;
}

double svtod(swoc::TextView text, swoc::TextView *parsed) {
// @return 10^e
auto pow10 = [](int e) -> double {
double zret = 1.0;
double scale = 10.0;
if (e < 0) { // flip the scale and make @a e positive.
e = -e;
scale = 0.1;
}

// Walk the bits in the exponent @a e and multiply the scale for set bits.
while (e) {
if (e & 1) {
zret *= scale;
}
scale *= scale;
e >>= 1;
}
return zret;
};

if (text.empty()) {
return 0;
}

auto org_text = text; // save this to update @a parsed.
// Check just once and dump to a local copy if needed.
TextView local_parsed;
if (! parsed) {
parsed = &local_parsed;
}

// Handle leading sign.
int sign = 1;
if (*text == '-') {
++text;
sign = -1;
} else if (*text == '+') {
++text;
}
// Parse the leading whole part as an integer.
intmax_t whole = svto_radix<10>(text);
parsed->assign(org_text.data(), text.data());

if (text.empty()) {
return whole;
}

double frac = 0.0;
if (*text == '.') { // fractional part.
++text;
double scale = 0.1;
while (text && isdigit(*text)) {
frac += scale * (*text++ - '0');
scale /= 10.0;
}
}

double exp = 1.0;
if (text.starts_with_nocase("e")) {
int exp_sign = 1;
++text;
if (text) {
if (*text == '+') {
++text;
} else if (*text == '-') {
++text;
exp_sign = -1;
}
}
auto exp_part = svto_radix<10>(text);
exp = pow10(exp_part * exp_sign);
}

parsed->assign(org_text.data(), text.data());
return sign * (whole + frac) * exp;
}

// Do the template instantiations.
template std::ostream& TextView::stream_write(std::ostream&, const TextView&) const;

Expand Down
3 changes: 3 additions & 0 deletions unit_tests/test_IntrusiveHashMap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,6 @@ TEST_CASE("IntrusiveHashMapManyStrings", "[IntrusiveHashMap]")
}
REQUIRE(miss_p == false);
};

TEST_CASE("IntrusiveHashMap Utilities", "[IntrusiveHashMap]") {
}
22 changes: 22 additions & 0 deletions unit_tests/test_TextView.cc
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,28 @@ TEST_CASE("TextView Conversions", "[libswoc][TextView]")
x = n3;
REQUIRE(25 == swoc::svto_radix<8>(x));
REQUIRE(x.size() == 0);

// floating point is never exact, so "good enough" is all that is measureable. This checks the
// value is within one epsilon (minimum change possible) of the compiler generated value.
auto fcmp = [](double lhs, double rhs) {
double tolerance = std::max( { 1.0, std::fabs(lhs) , std::fabs(rhs) } ) * std::numeric_limits<double>::epsilon();
return std::fabs(lhs - rhs) <= tolerance ;
};

REQUIRE(1.0 == swoc::svtod("1.0"));
REQUIRE(2.0 == swoc::svtod("2.0"));
REQUIRE(true == fcmp(0.1, swoc::svtod("0.1")));
REQUIRE(true == fcmp(0.1, swoc::svtod(".1")));
REQUIRE(true == fcmp(0.02, swoc::svtod("0.02")));
REQUIRE(true == fcmp(2.718281828, swoc::svtod("2.718281828")));
REQUIRE(true == fcmp(-2.718281828, swoc::svtod("-2.718281828")));
REQUIRE(true == fcmp(2.718281828, swoc::svtod("+2.718281828")));
REQUIRE(true == fcmp(0.004, swoc::svtod("4e-3")));
REQUIRE(true == fcmp(4e-3, swoc::svtod("4e-3")));
REQUIRE(true == fcmp(500000, swoc::svtod("5e5")));
REQUIRE(true == fcmp(5e5, swoc::svtod("5e+5")));
REQUIRE(true == fcmp(678900, swoc::svtod("6.789E5")));
REQUIRE(true == fcmp(6.789e5, swoc::svtod("6.789E+5")));
}

TEST_CASE("TransformView", "[libswoc][TransformView]")
Expand Down

0 comments on commit c47ba26

Please sign in to comment.