Created
February 8, 2018 22:19
-
-
Save gabyx/cd8ea9e727f5683a1fc654020edb6951 to your computer and use it in GitHub Desktop.
Fast String Conversion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <type_traits> | |
#include <iomanip> | |
#define white_space(c) ((c) == ' ' || (c) == '\t') | |
#define valid_digit(c) ((c) >= '0' && (c) <= '9') | |
namespace StringConversion | |
{ | |
namespace detail | |
{ | |
// integral implementation | |
template <typename T> | |
typename std::enable_if<std::is_integral<T>::value, bool>::type toType(T& r, const char* p) | |
{ | |
r = 0; | |
// Skip leading white space, if any. | |
while (white_space(*p)) | |
{ | |
p += 1; | |
} | |
// Get the sign! | |
bool neg = false; | |
if (*p == '-') | |
{ | |
neg = true; | |
++p; | |
} | |
else if (*p == '+') | |
{ | |
neg = false; | |
++p; | |
} | |
int c = 0; // counter to check how many numbers we got! | |
while (valid_digit(*p)) | |
{ | |
r = (r * 10) + (*p - '0'); | |
++p; | |
++c; | |
} | |
// FIRST CHECK: | |
if (c == 0) | |
{ | |
return false; | |
} // we got no dezimal places: invalid number! | |
// POST CHECK: | |
// skip post whitespaces | |
while (white_space(*p)) | |
{ | |
++p; | |
} | |
if (*p != '\0') | |
{ | |
return false; | |
} // if next character is not the terminating character: invalid number! | |
if (neg) | |
{ | |
r = -r; | |
} | |
return true; | |
} | |
// double,float implementation | |
template <typename T> | |
typename std::enable_if<std::is_floating_point<T>::value, bool>::type toType(T& r, const char* p) | |
{ | |
// Skip leading white space, if any. | |
while (white_space(*p)) | |
{ | |
p += 1; | |
} | |
r = 0.0; | |
int c = 0; // counter to check how many numbers we got! | |
// Get the sign! | |
bool neg = false; | |
if (*p == '-') | |
{ | |
neg = true; | |
++p; | |
} | |
else if (*p == '+') | |
{ | |
neg = false; | |
++p; | |
} | |
// Get the digits before decimal point | |
while (valid_digit(*p)) | |
{ | |
r = (r * 10.0) + (*p - '0'); | |
++p; | |
++c; | |
} | |
// Get the digits after decimal point | |
if (*p == '.') | |
{ | |
T f = 0.0; | |
T scale = 1.0; | |
++p; | |
while (*p >= '0' && *p <= '9') | |
{ | |
f = (f * 10.0) + (*p - '0'); | |
++p; | |
scale *= 10.0; | |
++c; | |
} | |
r += f / scale; | |
} | |
// FIRST CHECK: | |
if (c == 0) | |
{ | |
return false; | |
} // we got no dezimal places: invalid number! | |
// Get the digits after the "e"/"E" (exponenet) | |
if (*p == 'e' || *p == 'E') | |
{ | |
unsigned int e = 0; | |
bool negE = false; | |
++p; | |
if (*p == '-') | |
{ | |
negE = true; | |
++p; | |
} | |
else if (*p == '+') | |
{ | |
negE = false; | |
++p; | |
} | |
// Get exponent | |
c = 0; | |
while (valid_digit(*p)) | |
{ | |
e = (e * 10) + (*p - '0'); | |
++p; | |
++c; | |
} | |
// Check exponent limits! | |
// if( !neg && e>std::numeric_limits<T>::max_exponent10 ){ | |
// e = std::numeric_limits<T>::max_exponent10; | |
// }else if(e < std::numeric_limits<T>::min_exponent10 ){ | |
// e = std::numeric_limits<T>::max_exponent10; | |
// } | |
// SECOND CHECK: | |
if (c == 0) | |
{ | |
return false; | |
} // we got no exponent: invalid number! | |
T scaleE = 1.0; | |
// Calculate scaling factor. | |
while (e >= 50) | |
{ | |
scaleE *= 1E50; | |
e -= 50; | |
} | |
// while (e >= 8) { scaleE *= 1E8; e -= 8; } | |
while (e > 0) | |
{ | |
scaleE *= 10.0; | |
e -= 1; | |
} | |
if (negE) | |
{ | |
r /= scaleE; | |
} | |
else | |
{ | |
r *= scaleE; | |
} | |
} | |
// POST CHECK: | |
// skip post whitespaces | |
while (white_space(*p)) | |
{ | |
++p; | |
} | |
if (*p != '\0') | |
{ | |
return false; | |
} // if next character is not the terminating character: invalid number! | |
// Apply sign to number | |
if (neg) | |
{ | |
r = -r; | |
} | |
return true; | |
} | |
}; // detail | |
template <typename T> | |
inline bool toType(T& r, const std::string& s) | |
{ | |
return detail::toType(r, s.c_str()); | |
} | |
// Sepcial for boolean values | |
inline bool toType(bool& t, const std::string& s) | |
{ | |
char a; | |
if (detail::toType(a, s.c_str())) | |
{ | |
if (a) | |
{ | |
t = true; | |
return true; | |
} | |
else | |
{ | |
t = false; | |
return true; | |
} | |
} | |
if (s == "true") | |
{ | |
t = true; | |
return true; | |
} | |
else if (s == "false") | |
{ | |
t = false; | |
return true; | |
} | |
return false; | |
} | |
}; // StringConversion | |
int main() | |
{ | |
const char* arr[] = {"3.12312412E-6", "3.1238asd123u98123-6", "asd.asdasd", "3.1231231231247e-123"}; | |
for(auto* c : arr){ | |
double d; | |
StringConversion::toType(d,c); | |
std::cout <<std::setprecision(20) << d << std::endl; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment