Skip to content

Instantly share code, notes, and snippets.

@gabyx
Created February 8, 2018 22:19
Show Gist options
  • Save gabyx/cd8ea9e727f5683a1fc654020edb6951 to your computer and use it in GitHub Desktop.
Save gabyx/cd8ea9e727f5683a1fc654020edb6951 to your computer and use it in GitHub Desktop.
Fast String Conversion
#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