|
Cppcheck
|
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 }
1.7.6.1