Cppcheck
mathlib.cpp
Go to the documentation of this file.
00001 /*
00002  * Cppcheck - A tool for static C/C++ code analysis
00003  * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team.
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation, either version 3 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 
00020 
00021 #include "mathlib.h"
00022 #include "errorlogger.h"
00023 
00024 #include <string>
00025 #include <sstream>
00026 #include <cstdlib>
00027 #include <cmath>
00028 #include <cctype>
00029 
00030 MathLib::bigint MathLib::toLongNumber(const std::string &str)
00031 {
00032     // hexadecimal numbers:
00033     if (isHex(str)) {
00034         if (str[0] == '-') {
00035             bigint ret = 0;
00036             std::istringstream istr(str);
00037             istr >> std::hex >> ret;
00038             return ret;
00039         } else {
00040             unsigned long long ret = 0;
00041             std::istringstream istr(str);
00042             istr >> std::hex >> ret;
00043             return (bigint)ret;
00044         }
00045     }
00046 
00047     // octal numbers:
00048     if (isOct(str)) {
00049         bigint ret = 0;
00050         std::istringstream istr(str);
00051         istr >> std::oct >> ret;
00052         return ret;
00053     }
00054 
00055     // binary numbers:
00056     if (isBin(str)) {
00057         bigint ret = 0;
00058         for (std::string::size_type i = str[0] == '0'?2:3; i < str.length(); i++) {
00059             ret <<= 1;
00060             if (str[i] == '1')
00061                 ret |= 1;
00062         }
00063         if (str[0] == '-')
00064             ret = -ret;
00065         return ret;
00066     }
00067 
00068     if (str.find_first_of("eE") != std::string::npos)
00069         return static_cast<bigint>(std::atof(str.c_str()));
00070 
00071     bigint ret = 0;
00072     std::istringstream istr(str);
00073     istr >> ret;
00074     return ret;
00075 }
00076 
00077 std::string MathLib::longToString(const bigint value)
00078 {
00079     std::ostringstream result;
00080     result << value;
00081     return result.str();
00082 }
00083 
00084 double MathLib::toDoubleNumber(const std::string &str)
00085 {
00086     if (isHex(str))
00087         return static_cast<double>(toLongNumber(str));
00088     // nullcheck
00089     else if (isNullValue(str))
00090         return 0.0;
00091     // otherwise, convert to double
00092     std::istringstream istr(str);
00093     double ret;
00094     istr >> ret;
00095     return ret;
00096 }
00097 
00098 std::string MathLib::doubleToString(const double value)
00099 {
00100     std::ostringstream result;
00101     result.precision(12);
00102     result << value;
00103     if (result.str() == "-0")
00104         return "0.0";
00105     if (result.str().find(".") == std::string::npos)
00106         return result.str() + ".0";
00107     return result.str();
00108 }
00109 
00110 bool MathLib::isFloat(const std::string &s)
00111 {
00112     // every number that contains a . is a float
00113     if (s.find("." , 0) != std::string::npos)
00114         return true;
00115     // scientific notation
00116     return(s.find("E-", 0) != std::string::npos
00117            || s.find("e-", 0) != std::string::npos);
00118 }
00119 
00120 bool MathLib::isNegative(const std::string &s)
00121 {
00122     // remember position
00123     std::string::size_type n = 0;
00124     // eat up whitespace
00125     while (std::isspace(s[n])) ++n;
00126     // every negative number has a negative sign
00127     return(s[n] == '-');
00128 }
00129 
00130 bool MathLib::isOct(const std::string& str)
00131 {
00132     bool sign = str[0]=='-' || str[0]=='+';
00133     return(str[sign?1:0] == '0' && (str.size() == 1 || isOctalDigit(str[sign?2:1])) && !isFloat(str));
00134 }
00135 
00136 bool MathLib::isHex(const std::string& str)
00137 {
00138     bool sign = str[0]=='-' || str[0]=='+';
00139     return(str.compare(sign?1:0, 2, "0x") == 0 || str.compare(sign?1:0, 2, "0X") == 0);
00140 }
00141 
00142 bool MathLib::isBin(const std::string& str)
00143 {
00144     bool sign = str[0]=='-' || str[0]=='+';
00145     return((str.compare(sign?1:0, 2, "0b") == 0 || str.compare(sign?1:0, 2, "0B") == 0) && str.find_first_not_of("10bB", 1) == std::string::npos);
00146 }
00147 
00148 bool MathLib::isInt(const std::string & s)
00149 {
00150     // perform prechecks:
00151     // ------------------
00152     // first check, if a point is found, it is an floating point value
00153     if (s.find(".", 0) != std::string::npos) return false;
00154     // check for scientific notation e.g. NumberE-Number this is obvious an floating point value
00155     else if (s.find("E-", 0) != std::string::npos || s.find("e-", 0) != std::string::npos) return false;
00156 
00157 
00158     // prechecking has nothing found,...
00159     // gather information
00160     enum Representation {
00161         eScientific = 0, // NumberE+Number or NumberENumber
00162         eOctal,          // starts with 0
00163         eHex,            // starts with 0x
00164         eDefault         // Numbers with a (possible) trailing u or U or l or L for unsigned or long datatypes
00165     };
00166     // create an instance
00167     Representation Mode = eDefault;
00168 
00169 
00170     // remember position
00171     unsigned long n = 0;
00172     // eat up whitespace
00173     while (std::isspace(s[n])) ++n;
00174 
00175     // determine type
00176     if (s.find("E", 0) != std::string::npos) {
00177         Mode = eScientific;
00178     } else if (isHex(s)) {
00179         Mode = eHex;
00180     } else if (isOct(s)) {
00181         Mode = eOctal;
00182     }
00183 
00184     // check sign
00185     if (s[n] == '-' || s[n] == '+') ++n;
00186 
00187     // check scientific notation
00188     if (Mode == eScientific) {
00189         // check digits
00190         while (std::isdigit(s[n])) ++n;
00191 
00192         // check scientific notation
00193         if (std::tolower(s[n]) == 'e') {
00194             ++n;
00195             // check positive exponent
00196             if (s[n] == '+') ++n;
00197             // floating pointer number e.g. 124E-2
00198             if (s[n] == '-') return false;
00199             // check digits of the exponent
00200             while (std::isdigit(s[n])) ++n;
00201         }
00202     }
00203     // check hex notation
00204     else if (Mode == eHex) {
00205         ++n; // 0
00206         ++n; // x
00207         while (std::isxdigit(s[n]))
00208             ++n;
00209 
00210         while (std::tolower(s[n]) == 'u' || std::tolower(s[n]) == 'l') ++n; // unsigned or long (long)
00211     }
00212     // check octal notation
00213     else if (Mode == eOctal) {
00214         ++n; // 0
00215         while (isOctalDigit(s[n]))
00216             ++n;
00217 
00218         while (std::tolower(s[n]) == 'u' || std::tolower(s[n]) == 'l') ++n; // unsigned or long (long)
00219     } else if (Mode == eDefault) {
00220         // starts with digit
00221         bool bStartsWithDigit=false;
00222         while (std::isdigit(s[n])) {
00223             bStartsWithDigit=true;
00224             ++n;
00225         };
00226 
00227         while (std::tolower(s[n]) == 'u' || std::tolower(s[n]) == 'l') ++n; // unsigned or long (long)
00228 
00229         if (!bStartsWithDigit)
00230             return false;
00231     }
00232     // eat up whitespace
00233     while (std::isspace(s[n]))
00234         ++n;
00235 
00236     // if everything goes good, we are at the end of the string and no digits/character
00237     // is here --> return true, but if something was found eg. 12E+12AA return false
00238     return(n >= s.length());
00239 }
00240 
00241 std::string MathLib::add(const std::string & first, const std::string & second)
00242 {
00243     if (MathLib::isInt(first) && MathLib::isInt(second)) {
00244         return longToString(toLongNumber(first) + toLongNumber(second));
00245     }
00246 
00247     double d1 = toDoubleNumber(first);
00248     double d2 = toDoubleNumber(second);
00249 
00250     int count = 0;
00251     while (d1 > 100000.0 * d2 && doubleToString(d1+d2)==first && ++count<5)
00252         d2 *= 10.0;
00253     while (d2 > 100000.0 * d1 && doubleToString(d1+d2)==second && ++count<5)
00254         d1 *= 10.0;
00255 
00256     return doubleToString(d1 + d2);
00257 }
00258 
00259 std::string MathLib::subtract(const std::string &first, const std::string &second)
00260 {
00261     if (MathLib::isInt(first) && MathLib::isInt(second)) {
00262         return longToString(toLongNumber(first) - toLongNumber(second));
00263     }
00264 
00265     if (first == second)
00266         return "0.0" ;
00267 
00268     double d1 = toDoubleNumber(first);
00269     double d2 = toDoubleNumber(second);
00270 
00271     int count = 0;
00272     while (d1 > 100000.0 * d2 && doubleToString(d1-d2)==first && ++count<5)
00273         d2 *= 10.0;
00274     while (d2 > 100000.0 * d1 && doubleToString(d1-d2)==second && ++count<5)
00275         d1 *= 10.0;
00276 
00277     return doubleToString(d1 - d2);
00278 }
00279 
00280 std::string MathLib::divide(const std::string &first, const std::string &second)
00281 {
00282     if (MathLib::isInt(first) && MathLib::isInt(second)) {
00283         bigint b = toLongNumber(second);
00284         if (b == 0)
00285             throw InternalError(0, "Internal Error: Division by zero");
00286         return longToString(toLongNumber(first) / b);
00287     }
00288     return doubleToString(toDoubleNumber(first) / toDoubleNumber(second));
00289 }
00290 
00291 std::string MathLib::multiply(const std::string &first, const std::string &second)
00292 {
00293     if (MathLib::isInt(first) && MathLib::isInt(second)) {
00294         return longToString(toLongNumber(first) * toLongNumber(second));
00295     }
00296     return doubleToString(toDoubleNumber(first) * toDoubleNumber(second));
00297 }
00298 
00299 std::string MathLib::mod(const std::string &first, const std::string &second)
00300 {
00301     if (MathLib::isInt(first) && MathLib::isInt(second)) {
00302         bigint b = toLongNumber(second);
00303         if (b == 0)
00304             throw InternalError(0, "Internal Error: Division by zero");
00305         return longToString(toLongNumber(first) % b);
00306     }
00307     return doubleToString(std::fmod(toDoubleNumber(first),toDoubleNumber(second)));
00308 }
00309 
00310 std::string MathLib::calculate(const std::string &first, const std::string &second, char action)
00311 {
00312     switch (action) {
00313     case '+':
00314         return MathLib::add(first, second);
00315 
00316     case '-':
00317         return MathLib::subtract(first, second);
00318 
00319     case '*':
00320         return MathLib::multiply(first, second);
00321 
00322     case '/':
00323         return MathLib::divide(first, second);
00324 
00325     case '%':
00326         return MathLib::mod(first, second);
00327 
00328     case '&':
00329         return MathLib::longToString(MathLib::toLongNumber(first) & MathLib::toLongNumber(second));
00330 
00331     case '|':
00332         return MathLib::longToString(MathLib::toLongNumber(first) | MathLib::toLongNumber(second));
00333 
00334     case '^':
00335         return MathLib::longToString(MathLib::toLongNumber(first) ^ MathLib::toLongNumber(second));
00336 
00337     default:
00338         throw InternalError(0, std::string("Unexpected action '") + action + "' in MathLib::calculate(). Please report this to Cppcheck developers.");
00339     }
00340 }
00341 
00342 std::string MathLib::sin(const std::string &tok)
00343 {
00344     return doubleToString(std::sin(toDoubleNumber(tok)));
00345 }
00346 
00347 
00348 std::string MathLib::cos(const std::string &tok)
00349 {
00350     return doubleToString(std::cos(toDoubleNumber(tok)));
00351 }
00352 
00353 std::string MathLib::tan(const std::string &tok)
00354 {
00355     return doubleToString(std::tan(toDoubleNumber(tok)));
00356 }
00357 
00358 
00359 std::string MathLib::abs(const std::string &tok)
00360 {
00361     return doubleToString(std::abs(toDoubleNumber(tok)));
00362 }
00363 
00364 bool MathLib::isEqual(const std::string &first, const std::string &second)
00365 {
00366     // this conversion is needed for formatting
00367     // e.g. if first=0.1 and second=1.0E-1, the direct comparison of the strings whould fail
00368     return doubleToString(toDoubleNumber(first)) == doubleToString(toDoubleNumber(second));
00369 }
00370 
00371 bool MathLib::isNotEqual(const std::string &first, const std::string &second)
00372 {
00373     return !isEqual(first, second);
00374 }
00375 
00376 bool MathLib::isGreater(const std::string &first, const std::string &second)
00377 {
00378     return toDoubleNumber(first) > toDoubleNumber(second);
00379 }
00380 
00381 bool MathLib::isGreaterEqual(const std::string &first, const std::string &second)
00382 {
00383     return toDoubleNumber(first) >= toDoubleNumber(second);
00384 }
00385 
00386 bool MathLib::isLess(const std::string &first, const std::string &second)
00387 {
00388     return toDoubleNumber(first) < toDoubleNumber(second);
00389 }
00390 
00391 bool MathLib::isLessEqual(const std::string &first, const std::string &second)
00392 {
00393     return toDoubleNumber(first) <= toDoubleNumber(second);
00394 }
00395 
00396 bool MathLib::isNullValue(const std::string &str)
00397 {
00398     return (str == "-0"        || str == "0"      || str == "+0"
00399             || str == "-0.0"   || str == "0.0"    || str == "+0.0"
00400             || str == "-0."    || str == "+0."
00401             || str == "-0E-00" || str == "-0E+00" || str == "+0E+00" || str == "+0E-00"
00402             || str == "-0e-00" || str == "-0e+00" || str == "+0e+00" || str == "+0e-00"
00403             || str == "-0E-0");
00404 }
00405 
00406 bool MathLib::isOctalDigit(char c)
00407 {
00408     return(c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7');
00409 }