Implement hexadecimal float conversion in MathLib::toDoubleNumber ind… (#1506)
* Implement hexadecimal float conversion in MathLib::toDoubleNumber independent of C99/C++17 * Refactor MathLib::isFloatHex and cure some false results
This commit is contained in:
parent
a90c56ad76
commit
a68086c959
116
lib/mathlib.cpp
116
lib/mathlib.cpp
|
@ -548,6 +548,55 @@ MathLib::bigint MathLib::toLongNumber(const std::string & str)
|
|||
return ret;
|
||||
}
|
||||
|
||||
// in-place conversion of (sub)string to double. Requires no heap.
|
||||
static double myStod(const std::string& str, std::string::const_iterator from, std::string::const_iterator to, int base)
|
||||
{
|
||||
double result = 0.;
|
||||
bool positivesign = true;
|
||||
std::string::const_iterator it;
|
||||
if ('+' == *from) {
|
||||
it = from + 1;
|
||||
} else if ('-' == *from) {
|
||||
it = from + 1;
|
||||
positivesign = false;
|
||||
} else
|
||||
it = from;
|
||||
const std::size_t decimalsep = str.find('.', it-str.begin());
|
||||
int distance;
|
||||
if (std::string::npos == decimalsep) {
|
||||
distance = to - it;
|
||||
} else if (decimalsep > (to - str.begin()))
|
||||
return 0.; // error handling??
|
||||
else
|
||||
distance = int(decimalsep)-(from - str.begin());
|
||||
auto digitval = [&](char c) {
|
||||
if ((10 < base) && (c > '9'))
|
||||
return 10 + std::tolower(c) - 'a';
|
||||
else
|
||||
return c - '0';
|
||||
};
|
||||
for (; it!=to; ++it) {
|
||||
if ('.' == *it)
|
||||
continue;
|
||||
else
|
||||
--distance;
|
||||
result += digitval(*it)* std::pow(base, distance);
|
||||
}
|
||||
return (positivesign)?result:-result;
|
||||
}
|
||||
|
||||
|
||||
// Assuming a limited support of built-in hexadecimal floats (see C99, C++17) that is a fall-back implementation.
|
||||
// Performance has been optimized WRT to heap activity, however the calculation part is not optimized.
|
||||
static double FloatHexToDoubleNumber(const std::string& str)
|
||||
{
|
||||
const std::size_t p = str.find_first_of("pP",3);
|
||||
const double factor1 = myStod(str, str.begin() + 2, str.begin()+p, 16);
|
||||
const bool suffix = (str.back() == 'f') || (str.back() == 'F') || (str.back() == 'l') || (str.back() == 'L');
|
||||
const double exponent = myStod(str, str.begin() + p + 1, (suffix)?str.end()-1:str.end(), 10);
|
||||
const double factor2 = std::pow(2, exponent);
|
||||
return factor1 * factor2;
|
||||
}
|
||||
|
||||
double MathLib::toDoubleNumber(const std::string &str)
|
||||
{
|
||||
|
@ -563,6 +612,8 @@ double MathLib::toDoubleNumber(const std::string &str)
|
|||
// TODO : handle locale
|
||||
return std::strtod(str.c_str(), nullptr);
|
||||
#endif
|
||||
if (isFloatHex(str))
|
||||
return FloatHexToDoubleNumber(str);
|
||||
// otherwise, convert to double
|
||||
std::istringstream istr(str);
|
||||
istr.imbue(std::locale::classic());
|
||||
|
@ -794,69 +845,76 @@ bool MathLib::isIntHex(const std::string& str)
|
|||
bool MathLib::isFloatHex(const std::string& str)
|
||||
{
|
||||
enum Status {
|
||||
START, PLUSMINUS, HEX_PREFIX, WHOLE_NUMBER_DIGIT, WHOLE_NUMBER_DIGITS, FRACTION, EXPONENT_DIGIT, EXPONENT_DIGITS
|
||||
START, HEX_0, HEX_X, WHOLE_NUMBER_DIGIT, POINT, FRACTION, EXPONENT_P, EXPONENT_SIGN, EXPONENT_DIGITS, EXPONENT_SUFFIX
|
||||
} state = START;
|
||||
for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) {
|
||||
switch (state) {
|
||||
case START:
|
||||
if (*it == '+' || *it == '-')
|
||||
state = PLUSMINUS;
|
||||
else if (*it == '0')
|
||||
state = HEX_PREFIX;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case PLUSMINUS:
|
||||
if (*it == '0')
|
||||
state = HEX_PREFIX;
|
||||
state = HEX_0;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case HEX_PREFIX:
|
||||
case HEX_0:
|
||||
if (*it == 'x' || *it == 'X')
|
||||
state = HEX_X;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case HEX_X:
|
||||
if (isxdigit(static_cast<unsigned char>(*it)))
|
||||
state = WHOLE_NUMBER_DIGIT;
|
||||
else if (*it == '.')
|
||||
state = POINT;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case WHOLE_NUMBER_DIGIT:
|
||||
if (isxdigit(static_cast<unsigned char>(*it)))
|
||||
state = WHOLE_NUMBER_DIGITS;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case WHOLE_NUMBER_DIGITS:
|
||||
if (isxdigit(static_cast<unsigned char>(*it)))
|
||||
state = WHOLE_NUMBER_DIGITS;
|
||||
; // state = WHOLE_NUMBER_DIGITS;
|
||||
else if (*it=='.')
|
||||
state = FRACTION;
|
||||
else if (*it=='p' || *it=='P')
|
||||
state = EXPONENT_DIGIT;
|
||||
state = EXPONENT_P;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case POINT:
|
||||
case FRACTION:
|
||||
if (isxdigit(static_cast<unsigned char>(*it)))
|
||||
state = FRACTION;
|
||||
else if (*it=='p' || *it=='P')
|
||||
state = EXPONENT_DIGIT;
|
||||
state=FRACTION;
|
||||
else if (*it == 'p' || *it == 'P')
|
||||
state = EXPONENT_P;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case EXPONENT_DIGIT:
|
||||
if (isxdigit(static_cast<unsigned char>(*it)))
|
||||
case EXPONENT_P:
|
||||
if (isdigit(static_cast<unsigned char>(*it)))
|
||||
state = EXPONENT_DIGITS;
|
||||
else if (*it=='+' || *it=='-')
|
||||
else if (*it == '+' || *it == '-')
|
||||
state = EXPONENT_SIGN;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case EXPONENT_SIGN:
|
||||
if (isdigit(static_cast<unsigned char>(*it)))
|
||||
state = EXPONENT_DIGITS;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case EXPONENT_DIGITS:
|
||||
if (isxdigit(static_cast<unsigned char>(*it)))
|
||||
state = EXPONENT_DIGITS;
|
||||
if (isdigit(static_cast<unsigned char>(*it)))
|
||||
; // state = EXPONENT_DIGITS;
|
||||
else if (*it == 'f' || *it == 'F' || *it == 'l' || *it == 'L')
|
||||
state = EXPONENT_SUFFIX;
|
||||
else
|
||||
return *it=='f'||*it=='F'||*it=='l'||*it=='L';
|
||||
return false;
|
||||
break;
|
||||
case EXPONENT_SUFFIX:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return state==EXPONENT_DIGITS;
|
||||
return (EXPONENT_DIGITS==state) || (EXPONENT_SUFFIX == state);
|
||||
}
|
||||
|
||||
bool MathLib::isValidIntegerSuffix(const std::string& str)
|
||||
|
|
|
@ -371,6 +371,15 @@ private:
|
|||
ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0.0"), 0.000001);
|
||||
ASSERT_EQUALS_DOUBLE('0', MathLib::toDoubleNumber("'0'"), 0.000001);
|
||||
|
||||
ASSERT_EQUALS_DOUBLE(192, MathLib::toDoubleNumber("0x0.3p10"), 0.000001);
|
||||
ASSERT_EQUALS_DOUBLE(5.42101e-20, MathLib::toDoubleNumber("0x1p-64"), 1e-20);
|
||||
ASSERT_EQUALS_DOUBLE(3.14159, MathLib::toDoubleNumber("0x1.921fb5p+1"), 0.000001);
|
||||
ASSERT_EQUALS_DOUBLE(2006, MathLib::toDoubleNumber("0x1.f58000p+10"), 0.000001);
|
||||
ASSERT_EQUALS_DOUBLE(1e-010, MathLib::toDoubleNumber("0x1.b7cdfep-34"), 0.000001);
|
||||
ASSERT_EQUALS_DOUBLE(.484375, MathLib::toDoubleNumber("0x1.fp-2"), 0.000001);
|
||||
ASSERT_EQUALS_DOUBLE(9.0, MathLib::toDoubleNumber("0x1.2P3"), 0.000001);
|
||||
ASSERT_EQUALS_DOUBLE(0.0625, MathLib::toDoubleNumber("0x.1P0"), 0.000001);
|
||||
|
||||
// verify: string --> double --> string conversion
|
||||
ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("1.0f")));
|
||||
ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("1.0")));
|
||||
|
@ -598,7 +607,6 @@ private:
|
|||
|
||||
void isFloatHex() const {
|
||||
// hex number syntax: [sign]0x[hexnumbers][suffix]
|
||||
ASSERT_EQUALS(false, MathLib::isFloatHex(""));
|
||||
ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.999999999999ap-4"));
|
||||
ASSERT_EQUALS(true, MathLib::isFloatHex("0x0.3p10"));
|
||||
ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.fp3"));
|
||||
|
@ -606,7 +614,13 @@ private:
|
|||
ASSERT_EQUALS(true, MathLib::isFloatHex("0xcc.ccccccccccdp-11"));
|
||||
ASSERT_EQUALS(true, MathLib::isFloatHex("0x3.243F6A88p+03"));
|
||||
ASSERT_EQUALS(true, MathLib::isFloatHex("0xA.Fp-10"));
|
||||
ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p-10f"));
|
||||
ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10F"));
|
||||
ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10l"));
|
||||
ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10L"));
|
||||
ASSERT_EQUALS(true, MathLib::isFloatHex("0X.2p-0"));
|
||||
|
||||
ASSERT_EQUALS(false, MathLib::isFloatHex(""));
|
||||
ASSERT_EQUALS(false, MathLib::isFloatHex("0"));
|
||||
ASSERT_EQUALS(false, MathLib::isFloatHex("0x"));
|
||||
ASSERT_EQUALS(false, MathLib::isFloatHex("0xa"));
|
||||
|
@ -616,6 +630,10 @@ private:
|
|||
ASSERT_EQUALS(false, MathLib::isFloatHex("0x."));
|
||||
ASSERT_EQUALS(false, MathLib::isFloatHex("0XP"));
|
||||
ASSERT_EQUALS(false, MathLib::isFloatHex("0xx"));
|
||||
ASSERT_EQUALS(false, MathLib::isFloatHex("0x1P+-1"));
|
||||
ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+10e"));
|
||||
ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+1af"));
|
||||
ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+10LL"));
|
||||
}
|
||||
|
||||
void isIntHex() const {
|
||||
|
|
Loading…
Reference in New Issue