From 8bb5ac0efd99529584a6597fc5d9a85342886501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Wed, 7 Dec 2022 09:20:09 +0100 Subject: [PATCH] improved `MathLib::to{ULong|Long|Double}Number()` and increased test coverage / added CMake option `USE_LIBCXX` / fixed #10695 (#4611) --- .github/workflows/ubsan.yml | 2 +- cmake/compilerDefinitions.cmake | 12 +- cmake/compileroptions.cmake | 5 + cmake/options.cmake | 3 +- cmake/printInfo.cmake | 1 + lib/mathlib.cpp | 88 ++++++--- test/testmathlib.cpp | 335 +++++++++++++++++++++++++------- 7 files changed, 341 insertions(+), 105 deletions(-) diff --git a/.github/workflows/ubsan.yml b/.github/workflows/ubsan.yml index 344ba51b5..a67a4659b 100644 --- a/.github/workflows/ubsan.yml +++ b/.github/workflows/ubsan.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-22.04 env: - UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1 + UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1:report_error_type=1 steps: - uses: actions/checkout@v3 diff --git a/cmake/compilerDefinitions.cmake b/cmake/compilerDefinitions.cmake index 4a0d94ff2..1dddd81a2 100644 --- a/cmake/compilerDefinitions.cmake +++ b/cmake/compilerDefinitions.cmake @@ -13,11 +13,15 @@ if (MSVC) endif() # TODO: this should probably apply to the compiler and not the platform -if (CPPCHK_GLIBCXX_DEBUG AND UNIX) - # TODO: check if this can be enabled again for Clang - also done in Makefile - if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) +if (CPPCHK_GLIBCXX_DEBUG AND UNIX AND CMAKE_BUILD_TYPE STREQUAL "Debug") + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + if (USE_LIBCXX) + add_definitions(-DLIBCXX_ENABLE_DEBUG_MODE) + endif() + else() + # TODO: check if this can be enabled again for Clang - also done in Makefile add_definitions(-D_GLIBCXX_DEBUG) - endif() + endif() endif() if (HAVE_RULES) diff --git a/cmake/compileroptions.cmake b/cmake/compileroptions.cmake index f355fe45b..4b9a703c9 100644 --- a/cmake/compileroptions.cmake +++ b/cmake/compileroptions.cmake @@ -65,6 +65,11 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-gdwarf-4) endif() + if (USE_LIBCXX) + add_compile_options(-stdlib=libc++) + add_link_options(-lc++) + endif() + add_compile_options_safe(-Wno-documentation-unknown-command) # TODO: fix and enable these warnings - or move to suppression list below diff --git a/cmake/options.cmake b/cmake/options.cmake index 1f8f7342c..09954a9b1 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -42,9 +42,10 @@ option(USE_QT6 "Prefer Qt6 when available" option(HAVE_RULES "Usage of rules (needs PCRE library and headers)" OFF) option(USE_BUNDLED_TINYXML2 "Usage of bundled tinyxml2 library" ON) -option(CPPCHK_GLIBCXX_DEBUG "Usage of _GLIBCXX_DEBUG in Debug build" ON) +option(CPPCHK_GLIBCXX_DEBUG "Usage of STL debug checks in Debug build" ON) option(USE_THREADS "Usage of threads instead of fork() for -j" OFF) option(USE_BOOST "Usage of Boost" OFF) +option(USE_LIBCXX "Use libc++ instead of libstdc++" OFF) option(DISABLE_CRTDBG_MAP_ALLOC "Disable usage of Visual Studio C++ memory leak detection in Debug build" OFF) option(NO_UNIX_SIGNAL_HANDLING "Disable usage of Unix Signal Handling" OFF) diff --git a/cmake/printInfo.cmake b/cmake/printInfo.cmake index 0cad74590..af51ce599 100644 --- a/cmake/printInfo.cmake +++ b/cmake/printInfo.cmake @@ -76,6 +76,7 @@ if (USE_BOOST) message( STATUS "Boost_VERSION_STRING = ${Boost_VERSION_STRING}") message( STATUS "Boost_INCLUDE_DIRS = ${Boost_INCLUDE_DIRS}") endif() +message( STATUS "USE_LIBCXX = ${USE_LIBCXX}" ) message( STATUS ) if(${ANALYZE_ADDRESS}) diff --git a/lib/mathlib.cpp b/lib/mathlib.cpp index 5f0892ae3..330305ba7 100644 --- a/lib/mathlib.cpp +++ b/lib/mathlib.cpp @@ -293,8 +293,10 @@ MathLib::biguint MathLib::toULongNumber(const std::string & str) try { const biguint ret = std::stoull(str, nullptr, 16); return ret; - } catch (const std::out_of_range& e) { - throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: out_of_range: " + str + " (" + e.what() +")"); + } catch (const std::out_of_range& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: out_of_range: " + str); + } catch (const std::invalid_argument& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: invalid_argument: " + str); } } @@ -303,8 +305,10 @@ MathLib::biguint MathLib::toULongNumber(const std::string & str) try { const biguint ret = std::stoull(str, nullptr, 8); return ret; - } catch (const std::out_of_range& e) { - throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: out_of_range: " + str + " (" + e.what() +")"); + } catch (const std::out_of_range& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: out_of_range: " + str); + } catch (const std::invalid_argument& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: invalid_argument: " + str); } } @@ -318,26 +322,38 @@ MathLib::biguint MathLib::toULongNumber(const std::string & str) if (str[i] == '1') ret |= 1; } - /* if (str[0] == '-') - ret = -ret; */ + if (str[0] == '-') + ret = -ret; return ret; } if (isFloat(str)) { - // Things are going to be less precise now: the value can't b represented in the biguint type. + // Things are going to be less precise now: the value can't be represented in the biguint type. // Use min/max values as an approximation. See #5843 - const double doubleval = std::atof(str.c_str()); + // TODO: bail out when we are out of range? + const double doubleval = toDoubleNumber(str); if (doubleval > (double)std::numeric_limits::max()) return std::numeric_limits::max(); - else - return static_cast(doubleval); + else // cast to bigint to avoid UBSAN warning about negative double being out-of-range + return static_cast(static_cast(doubleval)); } + if (isCharLiteral(str)) + return simplecpp::characterLiteralToLL(str); + try { - const biguint ret = std::stoull(str, nullptr, 10); + std::size_t idx = 0; + const biguint ret = std::stoull(str, &idx, 10); + if (idx != str.size()) { + const std::string s = str.substr(idx); + if (s.find_first_not_of("LlUu") != std::string::npos && s != "i64" && s != "ui64") + throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: input was not completely consumed: " + str); + } return ret; - } catch (const std::out_of_range& e) { - throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: out_of_range: " + str + " (" + e.what() +")"); + } catch (const std::out_of_range& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: out_of_range: " + str); + } catch (const std::invalid_argument& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: invalid_argument: " + str); } } @@ -355,8 +371,10 @@ MathLib::bigint MathLib::toLongNumber(const std::string & str) try { const biguint ret = std::stoull(str, nullptr, 16); return (bigint)ret; - } catch (const std::out_of_range& e) { - throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: out_of_range: " + str + " (" + e.what() +")"); + } catch (const std::out_of_range& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: out_of_range: " + str); + } catch (const std::invalid_argument& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: invalid_argument: " + str); } } @@ -365,8 +383,10 @@ MathLib::bigint MathLib::toLongNumber(const std::string & str) try { const biguint ret = std::stoull(str, nullptr, 8); return ret; - } catch (const std::out_of_range& e) { - throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: out_of_range: " + str + " (" + e.what() +")"); + } catch (const std::out_of_range& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: out_of_range: " + str); + } catch (const std::invalid_argument& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: invalid_argument: " + str); } } @@ -388,6 +408,7 @@ MathLib::bigint MathLib::toLongNumber(const std::string & str) if (isFloat(str)) { // Things are going to be less precise now: the value can't be represented in the bigint type. // Use min/max values as an approximation. See #5843 + // TODO: bail out when we are out of range? const double doubleval = toDoubleNumber(str); if (doubleval > (double)std::numeric_limits::max()) return std::numeric_limits::max(); @@ -401,10 +422,18 @@ MathLib::bigint MathLib::toLongNumber(const std::string & str) return simplecpp::characterLiteralToLL(str); try { - const biguint ret = std::stoull(str, nullptr, 10); + std::size_t idx = 0; + const biguint ret = std::stoull(str, &idx, 10); + if (idx != str.size()) { + const std::string s = str.substr(idx); + if (s.find_first_not_of("LlUu") != std::string::npos && s != "i64" && s != "ui64") + throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: input was not completely consumed: " + str); + } return ret; - } catch (const std::out_of_range& e) { - throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: out_of_range: " + str + " (" + e.what() +")"); + } catch (const std::out_of_range& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: out_of_range: " + str); + } catch (const std::invalid_argument& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: invalid_argument: " + str); } } @@ -463,7 +492,7 @@ double MathLib::toDoubleNumber(const std::string &str) try { return simplecpp::characterLiteralToLL(str); } catch (const std::exception& e) { - throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: characterLiteralToLL(" + str + ") => " + e.what()); + throw InternalError(nullptr, "Internal Error. MathLib::toDoubleNumber: characterLiteralToLL(" + str + ") => " + e.what()); } } if (isIntHex(str)) @@ -479,7 +508,13 @@ double MathLib::toDoubleNumber(const std::string &str) std::istringstream istr(str); istr.imbue(std::locale::classic()); double ret; - istr >> ret; + if (!(istr >> ret)) + throw InternalError(nullptr, "Internal Error. MathLib::toDoubleNumber: conversion failed: " + str); + std::string s; + if (istr >> s) { + if (s.find_first_not_of("FfLl") != std::string::npos) + throw InternalError(nullptr, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: " + str); + } return ret; } @@ -488,11 +523,12 @@ template<> std::string MathLib::toString(double value) std::ostringstream result; result.precision(12); result << value; - if (result.str() == "-0") + std::string s = result.str(); + if (s == "-0") return "0.0"; - if (result.str().find('.') == std::string::npos) - return result.str() + ".0"; - return result.str(); + if (s.find_first_of(".e") == std::string::npos) + return s + ".0"; + return s; } bool MathLib::isFloat(const std::string &str) diff --git a/test/testmathlib.cpp b/test/testmathlib.cpp index db3992747..c651707e0 100644 --- a/test/testmathlib.cpp +++ b/test/testmathlib.cpp @@ -54,6 +54,7 @@ private: TEST_CASE(calculate1); TEST_CASE(typesuffix); TEST_CASE(toLongNumber); + TEST_CASE(toULongNumber); TEST_CASE(toDoubleNumber); TEST_CASE(naninf); TEST_CASE(isNullValue); @@ -145,13 +146,13 @@ private: ASSERT_EQUALS("5.0", MathLib::divide("25.5", "5.1")); ASSERT_EQUALS("7.0", MathLib::divide("21.", "3")); ASSERT_EQUALS("1", MathLib::divide("3", "2")); - ASSERT_THROW(MathLib::divide("123", "0"), InternalError); // decimal zero: throw - ASSERT_THROW(MathLib::divide("123", "00"), InternalError); // octal zero: throw - ASSERT_THROW(MathLib::divide("123", "0x0"), InternalError); // hex zero: throw + ASSERT_THROW_EQUALS(MathLib::divide("123", "0"), InternalError, "Internal Error: Division by zero"); // decimal zero: throw + ASSERT_THROW_EQUALS(MathLib::divide("123", "00"), InternalError, "Internal Error: Division by zero"); // octal zero: throw + ASSERT_THROW_EQUALS(MathLib::divide("123", "0x0"), InternalError, "Internal Error: Division by zero"); // hex zero: throw MathLib::divide("123", "0.0f"); // float zero: don't throw MathLib::divide("123", "0.0"); // double zero: don't throw MathLib::divide("123", "0.0L"); // long double zero: don't throw - ASSERT_THROW(MathLib::divide("-9223372036854775808", "-1"), InternalError); // #4520 - out of range => throw + ASSERT_THROW_EQUALS(MathLib::divide("-9223372036854775808", "-1"), InternalError, "Internal Error: Division overflow"); // #4520 - out of range => throw ASSERT_EQUALS("4611686018427387904", MathLib::divide("-9223372036854775808", "-2")); // #6679 @@ -166,7 +167,7 @@ private: ASSERT_EQUALS("1", MathLib::calculate("0", "1", '^')); // Unknown action should throw exception - ASSERT_THROW(MathLib::calculate("1","2",'j'),InternalError); + ASSERT_THROW_EQUALS(MathLib::calculate("1","2",'j'),InternalError, "Unexpected action 'j' in MathLib::calculate(). Please report this to Cppcheck developers."); } void calculate1() const { @@ -183,7 +184,7 @@ private: MathLib::calculate("123", "0.0", '%'); // don't throw #endif - ASSERT_THROW(MathLib::calculate("123", "0", '%'), InternalError); // throw + ASSERT_THROW_EQUALS(MathLib::calculate("123", "0", '%'), InternalError, "Internal Error: Division by zero"); // throw ASSERT_EQUALS("0", MathLib::calculate("1", "1", '^')); ASSERT_EQUALS("3", MathLib::calculate("2", "1", '^')); @@ -250,6 +251,33 @@ private: } void toLongNumber() const { + // zero input + ASSERT_EQUALS(0, MathLib::toLongNumber("0")); + ASSERT_EQUALS(0, MathLib::toLongNumber("-0")); + ASSERT_EQUALS(0, MathLib::toLongNumber("+0")); + ASSERT_EQUALS(0, MathLib::toLongNumber("0L")); + ASSERT_EQUALS(0, MathLib::toLongNumber("0l")); + ASSERT_EQUALS(0, MathLib::toLongNumber("0LL")); + ASSERT_EQUALS(0, MathLib::toLongNumber("0ll")); + ASSERT_EQUALS(0, MathLib::toLongNumber("0U")); + ASSERT_EQUALS(0, MathLib::toLongNumber("0u")); + ASSERT_EQUALS(0, MathLib::toLongNumber("0UL")); + ASSERT_EQUALS(0, MathLib::toLongNumber("0ul")); + ASSERT_EQUALS(0, MathLib::toLongNumber("0ULL")); + ASSERT_EQUALS(0, MathLib::toLongNumber("0ull")); + ASSERT_EQUALS(0, MathLib::toLongNumber("0i64")); // Visual Studio-specific + ASSERT_EQUALS(0, MathLib::toLongNumber("0ui64")); // Visual Studio-specific + + // TODO: needs to fail + //ASSERT_EQUALS(0, MathLib::toLongNumber("0lll")); + //ASSERT_EQUALS(0, MathLib::toLongNumber("0uu")); + + ASSERT_EQUALS(1U, MathLib::toLongNumber("1U")); + ASSERT_EQUALS(10000U, MathLib::toLongNumber("1e4")); + ASSERT_EQUALS(10000U, MathLib::toLongNumber("1e4")); + ASSERT_EQUALS(0xFF00000000000000UL, MathLib::toLongNumber("0xFF00000000000000UL")); + ASSERT_EQUALS(0x0A00000000000000UL, MathLib::toLongNumber("0x0A00000000000000UL")); + // from hex ASSERT_EQUALS(0, MathLib::toLongNumber("0x0")); ASSERT_EQUALS(0, MathLib::toLongNumber("-0x0")); @@ -279,6 +307,8 @@ private: ASSERT_EQUALS(1, MathLib::toLongNumber("0b1LLU")); ASSERT_EQUALS(1, MathLib::toLongNumber("+0b1")); ASSERT_EQUALS(-1, MathLib::toLongNumber("-0b1")); + ASSERT_EQUALS(9U, MathLib::toLongNumber("011")); + ASSERT_EQUALS(5U, MathLib::toLongNumber("0b101")); ASSERT_EQUALS(215, MathLib::toLongNumber("0b11010111")); ASSERT_EQUALS(-215, MathLib::toLongNumber("-0b11010111")); ASSERT_EQUALS(215, MathLib::toLongNumber("0B11010111")); @@ -305,30 +335,6 @@ private: ASSERT_EQUALS(-8552249625308161526, MathLib::toLongNumber("0x89504e470d0a1a0a")); ASSERT_EQUALS(-8481036456200365558, MathLib::toLongNumber("0x8a4d4e470d0a1a0a")); - ASSERT_EQUALS(9894494448401390090ULL, MathLib::toULongNumber("0x89504e470d0a1a0a")); - ASSERT_EQUALS(9965707617509186058ULL, MathLib::toULongNumber("0x8a4d4e470d0a1a0a")); - - // zero input - ASSERT_EQUALS(0, MathLib::toULongNumber("0")); - ASSERT_EQUALS(0, MathLib::toULongNumber("-0")); - ASSERT_EQUALS(0, MathLib::toULongNumber("+0")); - ASSERT_EQUALS(0U, MathLib::toULongNumber("0U")); - ASSERT_EQUALS(0, MathLib::toULongNumber("-0x0")); - - ASSERT_EQUALS(1U, MathLib::toULongNumber("1U")); - ASSERT_EQUALS(10000U, MathLib::toULongNumber("1e4")); - ASSERT_EQUALS(10000U, MathLib::toULongNumber("1e4")); - ASSERT_EQUALS(0xFF00000000000000UL, MathLib::toULongNumber("0xFF00000000000000UL")); - ASSERT_EQUALS(0x0A00000000000000UL, MathLib::toULongNumber("0x0A00000000000000UL")); - ASSERT_EQUALS(0, MathLib::toULongNumber("0b0")); - ASSERT_EQUALS(1, MathLib::toULongNumber("0b1")); - ASSERT_EQUALS(1, MathLib::toULongNumber("0b1U")); - ASSERT_EQUALS(1, MathLib::toULongNumber("0b1L")); - ASSERT_EQUALS(1, MathLib::toULongNumber("0b1LU")); - ASSERT_EQUALS(1, MathLib::toULongNumber("0b1LL")); - ASSERT_EQUALS(1, MathLib::toULongNumber("0b1LLU")); - ASSERT_EQUALS(9U, MathLib::toULongNumber("011")); - ASSERT_EQUALS(5U, MathLib::toULongNumber("0b101")); // from long long /* @@ -345,37 +351,20 @@ private: ASSERT_EQUALS(std::numeric_limits::min(), MathLib::toLongNumber(std::to_string(std::numeric_limits::min()))); ASSERT_EQUALS(std::numeric_limits::max(), MathLib::toLongNumber(std::to_string(std::numeric_limits::max()))); - ASSERT_EQUALS(std::numeric_limits::min(), MathLib::toULongNumber(std::to_string(std::numeric_limits::min()))); - ASSERT_EQUALS(std::numeric_limits::max(), MathLib::toULongNumber(std::to_string(std::numeric_limits::max()))); - ASSERT_EQUALS(std::numeric_limits::min(), MathLib::toULongNumber(std::to_string(std::numeric_limits::min()))); - ASSERT_EQUALS(std::numeric_limits::max(), MathLib::toULongNumber(std::to_string(std::numeric_limits::max()))); - // min/max and out-of-bounds - hex { const MathLib::bigint i = 0xFFFFFFFFFFFFFFFF; ASSERT_EQUALS(i, MathLib::toLongNumber(std::to_string(i))); ASSERT_EQUALS(i, MathLib::toLongNumber("0xFFFFFFFFFFFFFFFF")); } - { - const MathLib::biguint u = 0xFFFFFFFFFFFFFFFF; - ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); - ASSERT_EQUALS(u, MathLib::toULongNumber("0xFFFFFFFFFFFFFFFF")); - } { const MathLib::bigint i = -0xFFFFFFFFFFFFFFFF; ASSERT_EQUALS(i, MathLib::toLongNumber(std::to_string(i))); ASSERT_EQUALS(i, MathLib::toLongNumber("-0xFFFFFFFFFFFFFFFF")); } - { - const MathLib::biguint u = -0xFFFFFFFFFFFFFFFF; - ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); - ASSERT_EQUALS(u, MathLib::toULongNumber("-0xFFFFFFFFFFFFFFFF")); - } - ASSERT_THROW(MathLib::toLongNumber("0x10000000000000000"), InternalError); - ASSERT_THROW(MathLib::toULongNumber("0x10000000000000000"), InternalError); - ASSERT_THROW(MathLib::toLongNumber("-0x10000000000000000"), InternalError); - ASSERT_THROW(MathLib::toULongNumber("-0x10000000000000000"), InternalError); + ASSERT_THROW_EQUALS(MathLib::toLongNumber("0x10000000000000000"), InternalError, "Internal Error. MathLib::toLongNumber: out_of_range: 0x10000000000000000"); + ASSERT_THROW_EQUALS(MathLib::toLongNumber("-0x10000000000000000"), InternalError, "Internal Error. MathLib::toLongNumber: out_of_range: -0x10000000000000000"); // min/max and out-of-bounds - octal { @@ -383,26 +372,14 @@ private: ASSERT_EQUALS(i, MathLib::toLongNumber(std::to_string(i))); ASSERT_EQUALS(i, MathLib::toLongNumber("01777777777777777777777")); } - { - const MathLib::biguint u = 01777777777777777777777; - ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); - ASSERT_EQUALS(u, MathLib::toULongNumber("01777777777777777777777")); - } { const MathLib::bigint i = -01777777777777777777777; ASSERT_EQUALS(i, MathLib::toLongNumber(std::to_string(i))); ASSERT_EQUALS(i, MathLib::toLongNumber("-01777777777777777777777")); } - { - const MathLib::biguint u = -01777777777777777777777; - ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); - ASSERT_EQUALS(u, MathLib::toULongNumber("-01777777777777777777777")); - } - ASSERT_THROW(MathLib::toLongNumber("02000000000000000000000"), InternalError); - ASSERT_THROW(MathLib::toULongNumber("02000000000000000000000"), InternalError); - ASSERT_THROW(MathLib::toLongNumber("-02000000000000000000000"), InternalError); - ASSERT_THROW(MathLib::toULongNumber("-02000000000000000000000"), InternalError); + ASSERT_THROW_EQUALS(MathLib::toLongNumber("02000000000000000000000"), InternalError, "Internal Error. MathLib::toLongNumber: out_of_range: 02000000000000000000000"); + ASSERT_THROW_EQUALS(MathLib::toLongNumber("-02000000000000000000000"), InternalError, "Internal Error. MathLib::toLongNumber: out_of_range: -02000000000000000000000"); // min/max and out-of-bounds - decimal { @@ -410,26 +387,176 @@ private: ASSERT_EQUALS(i, MathLib::toLongNumber(std::to_string(i))); ASSERT_EQUALS(i, MathLib::toLongNumber("18446744073709551615")); } - { - const MathLib::biguint u = 18446744073709551615; - ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); - ASSERT_EQUALS(u, MathLib::toULongNumber("18446744073709551615")); - } { const MathLib::bigint i = -18446744073709551615; ASSERT_EQUALS(i, MathLib::toLongNumber(std::to_string(i))); ASSERT_EQUALS(i, MathLib::toLongNumber("-18446744073709551615")); } + + ASSERT_THROW_EQUALS(MathLib::toLongNumber("18446744073709551616"), InternalError, "Internal Error. MathLib::toLongNumber: out_of_range: 18446744073709551616"); + ASSERT_THROW_EQUALS(MathLib::toLongNumber("-18446744073709551616"), InternalError, "Internal Error. MathLib::toLongNumber: out_of_range: -18446744073709551616"); + + ASSERT_THROW_EQUALS(MathLib::toLongNumber("invalid"), InternalError, "Internal Error. MathLib::toLongNumber: invalid_argument: invalid"); + + ASSERT_THROW_EQUALS(MathLib::toLongNumber("1invalid"), InternalError, "Internal Error. MathLib::toLongNumber: input was not completely consumed: 1invalid"); + ASSERT_THROW_EQUALS(MathLib::toLongNumber("1 invalid"), InternalError, "Internal Error. MathLib::toLongNumber: input was not completely consumed: 1 invalid"); + + // TODO: test binary + // TODO: test floating point + + // TODO: test with 128-bit values + } + + void toULongNumber() const { + // zero input + ASSERT_EQUALS(0, MathLib::toULongNumber("0")); + ASSERT_EQUALS(0, MathLib::toULongNumber("-0")); + ASSERT_EQUALS(0, MathLib::toULongNumber("+0")); + ASSERT_EQUALS(0, MathLib::toULongNumber("0L")); + ASSERT_EQUALS(0, MathLib::toULongNumber("0l")); + ASSERT_EQUALS(0, MathLib::toULongNumber("0LL")); + ASSERT_EQUALS(0, MathLib::toULongNumber("0ll")); + ASSERT_EQUALS(0, MathLib::toULongNumber("0U")); + ASSERT_EQUALS(0, MathLib::toULongNumber("0u")); + ASSERT_EQUALS(0, MathLib::toULongNumber("0UL")); + ASSERT_EQUALS(0, MathLib::toULongNumber("0ul")); + ASSERT_EQUALS(0, MathLib::toULongNumber("0ULL")); + ASSERT_EQUALS(0, MathLib::toULongNumber("0ull")); + ASSERT_EQUALS(0, MathLib::toULongNumber("0i64")); // Visual Studio-specific + ASSERT_EQUALS(0, MathLib::toULongNumber("0ui64")); // Visual Studio-specific + + // TODO: needs to fail + //ASSERT_EQUALS(0, MathLib::toULongNumber("0lll")); + //ASSERT_EQUALS(0, MathLib::toULongNumber("0uu")); + + ASSERT_EQUALS(1U, MathLib::toULongNumber("1U")); + ASSERT_EQUALS(10000U, MathLib::toULongNumber("1e4")); + ASSERT_EQUALS(10000U, MathLib::toULongNumber("1e4")); + ASSERT_EQUALS(0xFF00000000000000UL, MathLib::toULongNumber("0xFF00000000000000UL")); + ASSERT_EQUALS(0x0A00000000000000UL, MathLib::toULongNumber("0x0A00000000000000UL")); + + // from hex + ASSERT_EQUALS(0, MathLib::toULongNumber("0x0")); + ASSERT_EQUALS(0, MathLib::toULongNumber("-0x0")); + ASSERT_EQUALS(0, MathLib::toULongNumber("+0x0")); + ASSERT_EQUALS(10, MathLib::toULongNumber("0xa")); + ASSERT_EQUALS(10995, MathLib::toULongNumber("0x2AF3")); + ASSERT_EQUALS(-10, MathLib::toULongNumber("-0xa")); + ASSERT_EQUALS(-10995, MathLib::toULongNumber("-0x2AF3")); + ASSERT_EQUALS(10, MathLib::toULongNumber("+0xa")); + ASSERT_EQUALS(10995, MathLib::toULongNumber("+0x2AF3")); + + // from octal + ASSERT_EQUALS(8, MathLib::toULongNumber("010")); + ASSERT_EQUALS(8, MathLib::toULongNumber("+010")); + ASSERT_EQUALS(-8, MathLib::toULongNumber("-010")); + ASSERT_EQUALS(125, MathLib::toULongNumber("0175")); + ASSERT_EQUALS(125, MathLib::toULongNumber("+0175")); + ASSERT_EQUALS(-125, MathLib::toULongNumber("-0175")); + + // from binary + ASSERT_EQUALS(0, MathLib::toULongNumber("0b0")); + ASSERT_EQUALS(1, MathLib::toULongNumber("0b1")); + ASSERT_EQUALS(1, MathLib::toULongNumber("0b1U")); + ASSERT_EQUALS(1, MathLib::toULongNumber("0b1L")); + ASSERT_EQUALS(1, MathLib::toULongNumber("0b1LU")); + ASSERT_EQUALS(1, MathLib::toULongNumber("0b1LL")); + ASSERT_EQUALS(1, MathLib::toULongNumber("0b1LLU")); + ASSERT_EQUALS(1, MathLib::toULongNumber("+0b1")); + ASSERT_EQUALS(-1, MathLib::toULongNumber("-0b1")); + ASSERT_EQUALS(9U, MathLib::toULongNumber("011")); + ASSERT_EQUALS(5U, MathLib::toULongNumber("0b101")); + ASSERT_EQUALS(215, MathLib::toULongNumber("0b11010111")); + ASSERT_EQUALS(-215, MathLib::toULongNumber("-0b11010111")); + ASSERT_EQUALS(215, MathLib::toULongNumber("0B11010111")); + + // from base 10 + ASSERT_EQUALS(10, MathLib::toULongNumber("10")); + ASSERT_EQUALS(10, MathLib::toULongNumber("10.")); + ASSERT_EQUALS(10, MathLib::toULongNumber("10.0")); + ASSERT_EQUALS(100, MathLib::toULongNumber("10E+1")); + ASSERT_EQUALS(1, MathLib::toULongNumber("10E-1")); + ASSERT_EQUALS(100, MathLib::toULongNumber("+10E+1")); + ASSERT_EQUALS(-1, MathLib::toULongNumber("-10E-1")); + ASSERT_EQUALS(100, MathLib::toULongNumber("+10.E+1")); + ASSERT_EQUALS(-1, MathLib::toULongNumber("-10.E-1")); + ASSERT_EQUALS(100, MathLib::toULongNumber("+10.0E+1")); + ASSERT_EQUALS(-1, MathLib::toULongNumber("-10.0E-1")); + + // from char + ASSERT_EQUALS((int)('A'), MathLib::toULongNumber("'A'")); + ASSERT_EQUALS((int)('\x10'), MathLib::toULongNumber("'\\x10'")); + ASSERT_EQUALS((int)('\100'), MathLib::toULongNumber("'\\100'")); + ASSERT_EQUALS((int)('\200'), MathLib::toULongNumber("'\\200'")); + ASSERT_EQUALS((int)(L'A'), MathLib::toULongNumber("L'A'")); + + ASSERT_EQUALS(9894494448401390090ULL, MathLib::toULongNumber("0x89504e470d0a1a0a")); + ASSERT_EQUALS(9965707617509186058ULL, MathLib::toULongNumber("0x8a4d4e470d0a1a0a")); + + // from long long + /* + * ASSERT_EQUALS(0xFF00000000000000LL, MathLib::toULongNumber("0xFF00000000000000LL")); + * This does not work in a portable way! + * While it succeeds on 32bit Visual Studio it fails on Linux 64bit because it is greater than 0x7FFFFFFFFFFFFFFF (=LLONG_MAX) + */ + + ASSERT_EQUALS(0x0A00000000000000LL, MathLib::toULongNumber("0x0A00000000000000LL")); + + // min/max numeric limits + ASSERT_EQUALS(std::numeric_limits::min(), MathLib::toULongNumber(std::to_string(std::numeric_limits::min()))); + ASSERT_EQUALS(std::numeric_limits::max(), MathLib::toULongNumber(std::to_string(std::numeric_limits::max()))); + ASSERT_EQUALS(std::numeric_limits::min(), MathLib::toULongNumber(std::to_string(std::numeric_limits::min()))); + ASSERT_EQUALS(std::numeric_limits::max(), MathLib::toULongNumber(std::to_string(std::numeric_limits::max()))); + + // min/max and out-of-bounds - hex + { + const MathLib::biguint u = 0xFFFFFFFFFFFFFFFF; + ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); + ASSERT_EQUALS(u, MathLib::toULongNumber("0xFFFFFFFFFFFFFFFF")); + } + { + const MathLib::biguint u = -0xFFFFFFFFFFFFFFFF; + ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); + ASSERT_EQUALS(u, MathLib::toULongNumber("-0xFFFFFFFFFFFFFFFF")); + } + + ASSERT_THROW_EQUALS(MathLib::toULongNumber("0x10000000000000000"), InternalError, "Internal Error. MathLib::toULongNumber: out_of_range: 0x10000000000000000"); + ASSERT_THROW_EQUALS(MathLib::toULongNumber("-0x10000000000000000"), InternalError, "Internal Error. MathLib::toULongNumber: out_of_range: -0x10000000000000000"); + + // min/max and out-of-bounds - octal + { + const MathLib::biguint u = 01777777777777777777777; + ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); + ASSERT_EQUALS(u, MathLib::toULongNumber("01777777777777777777777")); + } + { + const MathLib::biguint u = -01777777777777777777777; + ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); + ASSERT_EQUALS(u, MathLib::toULongNumber("-01777777777777777777777")); + } + + ASSERT_THROW_EQUALS(MathLib::toULongNumber("02000000000000000000000"), InternalError, "Internal Error. MathLib::toULongNumber: out_of_range: 02000000000000000000000"); + ASSERT_THROW_EQUALS(MathLib::toULongNumber("-02000000000000000000000"), InternalError, "Internal Error. MathLib::toULongNumber: out_of_range: -02000000000000000000000"); + + // min/max and out-of-bounds - decimal + { + const MathLib::biguint u = 18446744073709551615; + ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); + ASSERT_EQUALS(u, MathLib::toULongNumber("18446744073709551615")); + } { const MathLib::biguint u = -18446744073709551615; ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); ASSERT_EQUALS(u, MathLib::toULongNumber("-18446744073709551615")); } - ASSERT_THROW(MathLib::toLongNumber("18446744073709551616"), InternalError); - ASSERT_THROW(MathLib::toULongNumber("18446744073709551616"), InternalError); - ASSERT_THROW(MathLib::toLongNumber("-18446744073709551616"), InternalError); - ASSERT_THROW(MathLib::toULongNumber("-18446744073709551616"), InternalError); + ASSERT_THROW_EQUALS(MathLib::toULongNumber("18446744073709551616"), InternalError, "Internal Error. MathLib::toULongNumber: out_of_range: 18446744073709551616"); + ASSERT_THROW_EQUALS(MathLib::toULongNumber("-18446744073709551616"), InternalError, "Internal Error. MathLib::toULongNumber: out_of_range: -18446744073709551616"); + + ASSERT_THROW_EQUALS(MathLib::toULongNumber("invalid"), InternalError, "Internal Error. MathLib::toULongNumber: invalid_argument: invalid"); + + ASSERT_THROW_EQUALS(MathLib::toULongNumber("1invalid"), InternalError, "Internal Error. MathLib::toULongNumber: input was not completely consumed: 1invalid"); + ASSERT_THROW_EQUALS(MathLib::toULongNumber("1 invalid"), InternalError, "Internal Error. MathLib::toULongNumber: input was not completely consumed: 1 invalid"); // TODO: test binary // TODO: test floating point @@ -438,10 +565,22 @@ private: } void toDoubleNumber() const { - ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1"), 0.001); + // float values + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1."), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.f"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.F"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.l"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.L"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0f"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0F"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0l"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0L"), 0.001); ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("0x1"), 0.001); ASSERT_EQUALS_DOUBLE(10.0, MathLib::toDoubleNumber("10"), 0.001); ASSERT_EQUALS_DOUBLE(1000.0, MathLib::toDoubleNumber("10E+2"), 0.001); + ASSERT_EQUALS_DOUBLE(1000.0, MathLib::toDoubleNumber("10E+2l"), 0.001); + ASSERT_EQUALS_DOUBLE(1000.0, MathLib::toDoubleNumber("10E+2L"), 0.001); ASSERT_EQUALS_DOUBLE(100.0, MathLib::toDoubleNumber("1.0E+2"), 0.001); ASSERT_EQUALS_DOUBLE(-100.0, MathLib::toDoubleNumber("-1.0E+2"), 0.001); ASSERT_EQUALS_DOUBLE(-1e+10, MathLib::toDoubleNumber("-1.0E+10"), 1); @@ -449,6 +588,34 @@ private: ASSERT_EQUALS_DOUBLE(1e+10, MathLib::toDoubleNumber("+1.0E+10"), 1); ASSERT_EQUALS_DOUBLE(100.0, MathLib::toDoubleNumber("1.0E+2"), 0.001); ASSERT_EQUALS_DOUBLE(1e+10, MathLib::toDoubleNumber("1.0E+10"), 1); + + // valid non-float values + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1l"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1L"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1ll"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1LL"), 0.001); + // TODO: need to succeed + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1u"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1U"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1ul"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1UL"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1ULL"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1ULL"), 0.001); + + // TODO: need to fail + // invalid values + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1f"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1F"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.ff"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.FF"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.ll"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0LL"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0ff"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0FF"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0ll"), 0.001); + //ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0LL"), 0.001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E+0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E-0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E+00"), 0.000001); @@ -476,6 +643,25 @@ private: ASSERT_EQUALS_DOUBLE(9.0, MathLib::toDoubleNumber("0x1.2P3"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0625, MathLib::toDoubleNumber("0x.1P0"), 0.000001); + // from char + ASSERT_EQUALS_DOUBLE((double)('A'), MathLib::toDoubleNumber("'A'"), 0.000001); + ASSERT_EQUALS_DOUBLE((double)('\x10'), MathLib::toDoubleNumber("'\\x10'"), 0.000001); + ASSERT_EQUALS_DOUBLE((double)('\100'), MathLib::toDoubleNumber("'\\100'"), 0.000001); + ASSERT_EQUALS_DOUBLE((double)('\200'), MathLib::toDoubleNumber("'\\200'"), 0.000001); + ASSERT_EQUALS_DOUBLE((double)(L'A'), MathLib::toDoubleNumber("L'A'"), 0.000001); + + ASSERT_THROW_EQUALS(MathLib::toDoubleNumber("invalid"), InternalError, "Internal Error. MathLib::toDoubleNumber: conversion failed: invalid"); + +#ifdef _LIBCPP_VERSION + ASSERT_THROW_EQUALS(MathLib::toDoubleNumber("1invalid"), InternalError, "Internal Error. MathLib::toDoubleNumber: conversion failed: 1invalid"); + ASSERT_THROW_EQUALS(MathLib::toDoubleNumber("1.1invalid"), InternalError, "Internal Error. MathLib::toDoubleNumber: conversion failed: 1.1invalid"); +#else + ASSERT_THROW_EQUALS(MathLib::toDoubleNumber("1invalid"), InternalError, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1invalid"); + ASSERT_THROW_EQUALS(MathLib::toDoubleNumber("1.1invalid"), InternalError, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1.1invalid"); +#endif + ASSERT_THROW_EQUALS(MathLib::toDoubleNumber("1 invalid"), InternalError, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1 invalid"); + ASSERT_THROW_EQUALS(MathLib::toDoubleNumber("-1e-08.0"), InternalError, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: -1e-08.0"); + // 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"))); @@ -1230,6 +1416,9 @@ private: ASSERT_EQUALS("0.000000", MathLib::toString(0.0L)); ASSERT_EQUALS("0.000000", MathLib::toString(+0.0L)); ASSERT_EQUALS("-0.000000", MathLib::toString(-0.0L)); + + ASSERT_EQUALS("1e-08", MathLib::toString(0.00000001)); + ASSERT_EQUALS("-1e-08", MathLib::toString(-0.00000001)); } void CPP14DigitSeparators() const { // Ticket #7137, #7565