From 485e83653565a62d673bda218716c0d5814f6f0a Mon Sep 17 00:00:00 2001 From: PKEuS Date: Fri, 17 Feb 2012 19:54:53 +0100 Subject: [PATCH] Refactorizations in MathLib. Fixed recently introduced bug on floating point numbers with multiple preceding zeros (for example 004.123) --- lib/mathlib.cpp | 101 +++++++++++++++++++----------------------- lib/mathlib.h | 10 ++--- lib/tokenize.cpp | 6 +-- test/testtokenize.cpp | 5 ++- 4 files changed, 55 insertions(+), 67 deletions(-) diff --git a/lib/mathlib.cpp b/lib/mathlib.cpp index e54ea46b2..e51645297 100644 --- a/lib/mathlib.cpp +++ b/lib/mathlib.cpp @@ -29,10 +29,8 @@ MathLib::bigint MathLib::toLongNumber(const std::string &str) { - bool sign = str[0]=='-'||str[0]=='+'; // hexadecimal numbers: - if (str.compare(sign?1:0, 2, "0x") == 0 - || str.compare(sign?1:0, 2, "0X") == 0) { + if (isHex(str)) { bigint ret = 0; std::istringstream istr(str); istr >> std::hex >> ret; @@ -40,7 +38,7 @@ MathLib::bigint MathLib::toLongNumber(const std::string &str) } // octal numbers: - if (str[sign?1:0] == '0') { + if (isOct(str)) { bigint ret = 0; std::istringstream istr(str); istr >> std::oct >> ret; @@ -58,9 +56,8 @@ MathLib::bigint MathLib::toLongNumber(const std::string &str) double MathLib::toDoubleNumber(const std::string &str) { - if (str.compare(0, 2, "0x") == 0) { - return std::strtoul(str.c_str(), '\0', 16); - } + if (isHex(str)) + return static_cast(toLongNumber(str)); // nullcheck else if (isNullValue(str)) return 0.0; @@ -77,24 +74,30 @@ bool MathLib::isFloat(const std::string &s) if (s.find("." , 0) != std::string::npos) return true; // scientific notation - else if (s.find("E-", 0) != std::string::npos - || s.find("e-", 0) != std::string::npos) - return true; - - return false; + return(s.find("E-", 0) != std::string::npos + || s.find("e-", 0) != std::string::npos); } bool MathLib::isNegative(const std::string &s) { // remember position - unsigned long n = 0; + std::string::size_type n = 0; // eat up whitespace while (std::isspace(s[n])) ++n; // every negative number has a negative sign - if (s[n] == '-') - return true; + return(s[n] == '-'); +} - return false; +bool MathLib::isOct(const std::string& str) +{ + bool sign = str[0]=='-' || str[0]=='+'; + return(str[sign?1:0] == '0' && (str.size() == 1 || isOctalDigit(str[sign?2:1])) && !isFloat(str)); +} + +bool MathLib::isHex(const std::string& str) +{ + bool sign = str[0]=='-' || str[0]=='+'; + return(str.compare(sign?1:0, 2, "0x") == 0 || str.compare(sign?1:0, 2, "0X") == 0); } bool MathLib::isInt(const std::string & s) @@ -110,10 +113,10 @@ bool MathLib::isInt(const std::string & s) // prechecking has nothing found,... // gather information enum Representation { - eScientific = 0, // NumberE+Number or NumberENumber - eOctal, // starts with 0 - eHex, // starts with 0x - eDefault // Numbers with a (possible) trailing u or U or l or L for unsigned or long datatypes + eScientific = 0, // NumberE+Number or NumberENumber + eOctal, // starts with 0 + eHex, // starts with 0x + eDefault // Numbers with a (possible) trailing u or U or l or L for unsigned or long datatypes }; // create an instance Representation Mode = eDefault; @@ -127,9 +130,9 @@ bool MathLib::isInt(const std::string & s) // determine type if (s.find("E", 0) != std::string::npos) { Mode = eScientific; - } else if (s.find("0x", n, 2) != std::string::npos) { + } else if (isHex(s)) { Mode = eHex; - } else if (s.length() > 1 && s[0] == '0' && std::isdigit(s[1])) { + } else if (isOct(s)) { Mode = eOctal; } @@ -158,11 +161,16 @@ bool MathLib::isInt(const std::string & s) ++n; // x while (std::isxdigit(s[n])) ++n; + + while (std::tolower(s[n]) == 'u' || std::tolower(s[n]) == 'l') ++n; // unsigned or long (long) } // check octal notation else if (Mode == eOctal) { + ++n; // 0 while (isOctalDigit(s[n])) ++n; + + while (std::tolower(s[n]) == 'u' || std::tolower(s[n]) == 'l') ++n; // unsigned or long (long) } else if (Mode == eDefault) { // starts with digit bool bStartsWithDigit=false; @@ -170,10 +178,10 @@ bool MathLib::isInt(const std::string & s) bStartsWithDigit=true; ++n; }; - // unsigned or long - while (std::tolower(s[n]) == 'u' || std::tolower(s[n]) == 'l') ++n; - if (bStartsWithDigit==false) + while (std::tolower(s[n]) == 'u' || std::tolower(s[n]) == 'l') ++n; // unsigned or long (long) + + if (!bStartsWithDigit) return false; } // eat up whitespace @@ -182,10 +190,7 @@ bool MathLib::isInt(const std::string & s) // if everything goes good, we are at the end of the string and no digits/character // is here --> return true, but if something was found eg. 12E+12AA return false - if (s[n]) - return false; - return true; - + return(n >= s.length()); } std::string MathLib::add(const std::string & first, const std::string & second) @@ -222,30 +227,24 @@ std::string MathLib::multiply(const std::string &first, const std::string &secon std::string MathLib::calculate(const std::string &first, const std::string &second, char action) { - std::string result("0"); - switch (action) { case '+': - result = MathLib::add(first, second); - break; + return MathLib::add(first, second); case '-': - result = MathLib::subtract(first, second); - break; + return MathLib::subtract(first, second); case '*': - result = MathLib::multiply(first, second); - break; + return MathLib::multiply(first, second); case '/': - result = MathLib::divide(first, second); - break; + return MathLib::divide(first, second); default: throw InternalError(0, std::string("Unexpected action '") + action + "' in MathLib::calculate(). Please report this to Cppcheck developers."); } - return result; + return "0"; } std::string MathLib::sin(const std::string &tok) @@ -304,23 +303,15 @@ bool MathLib::isLessEqual(const std::string &first, const std::string &second) bool MathLib::isNullValue(const std::string &str) { - return (str == "-0" || str == "-0.0" - || str == "0" - || str == "-0." || str == "-0E-00" - || str == "-0E+00" || str == "+0E+00" - || str == "+0E-00" || str == "+0" - || str == "+0.0" || str == "+0." - || str == "0.0" - || str == "+0e+00" || str == "-0e+00" - || str == "+0e-00" || str == "-0e-00" - || str == "-0E-0"); + return (str == "-0" || str == "0" || str == "+0" + || str == "-0.0" || str == "0.0" || str == "+0.0" + || str == "-0." || str == "+0." + || str == "-0E-00" || str == "-0E+00" || str == "+0E+00" || str == "+0E-00" + || str == "-0e-00" || str == "-0e+00" || str == "+0e+00" || str == "+0e-00" + || str == "-0E-0"); } bool MathLib::isOctalDigit(char c) { - if (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7') - return true; - - return false; + return(c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7'); } - diff --git a/lib/mathlib.h b/lib/mathlib.h index c0499ddf0..c92c50659 100644 --- a/lib/mathlib.h +++ b/lib/mathlib.h @@ -28,8 +28,6 @@ /** @brief simple math functions that uses operands stored in std::string. useful when performing math on tokens. */ -class Tokenizer; - class MathLib { public: typedef long long bigint; @@ -41,11 +39,7 @@ public: static std::string toString(const T &d) { std::ostringstream result; result << d; - std::string strResult(result.str()); - if (strResult == "-0" - || strResult == "+0" - || strResult == "-0." - || strResult == "+0.") + if (isNullValue(result.str())) return std::string("0"); return result.str(); } @@ -53,6 +47,8 @@ public: static bool isInt(const std::string & str); static bool isFloat(const std::string &str); static bool isNegative(const std::string &str); + static bool isHex(const std::string& str); + static bool isOct(const std::string& str); static std::string add(const std::string & first, const std::string & second); static std::string subtract(const std::string & first, const std::string & second); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 5a35976e5..76582c975 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -117,7 +117,7 @@ void Tokenizer::addtoken(const char str[], const unsigned int lineno, const unsi // Replace hexadecimal value with decimal std::ostringstream str2; - if (strncmp(str, "0x", 2) == 0 || strncmp(str, "0X", 2) == 0 || (str[0] == '0' && std::isdigit(str[1]))) { + if (MathLib::isHex(str) || MathLib::isOct(str)) { str2 << MathLib::toLongNumber(str); } else if (strncmp(str, "_Bool", 5) == 0) { str2 << "bool"; @@ -374,9 +374,9 @@ void Tokenizer::createTokens(std::istream &code) } else if (strchr("+-", ch) && CurrentToken.length() > 0 && std::isdigit(CurrentToken[0]) && - CurrentToken.compare(0,2,"0x") != 0 && (CurrentToken[CurrentToken.length()-1] == 'e' || - CurrentToken[CurrentToken.length()-1] == 'E')) { + CurrentToken[CurrentToken.length()-1] == 'E') && + !MathLib::isHex(CurrentToken)) { // Don't separate doubles "4.2e+10" } else if (CurrentToken.empty() && ch == '.' && std::isdigit(code.peek())) { // tokenize .125 into 0.125 diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index d173a4da2..0c03b0be9 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -587,6 +587,7 @@ private: // Ticket #2429: 0.125 void tokenize15() { ASSERT_EQUALS("0.125", tokenizeAndStringify(".125")); + ASSERT_EQUALS("005.125", tokenizeAndStringify("005.125")); // Don't confuse with octal values } // #2612 - segfault for "<><<" @@ -683,7 +684,7 @@ private: void longtok() { - std::string filedata(10000, 'a'); + const std::string filedata(10000, 'a'); errout.str(""); @@ -695,7 +696,7 @@ private: tokenizer.tokenize(istr, "test.cpp"); // Expected result.. - ASSERT_EQUALS(std::string(10000, 'a'), tokenizer.tokens()->str()); + ASSERT_EQUALS(filedata, tokenizer.tokens()->str()); }