improved `MathLib::to{ULong|Long|Double}Number()` and increased test coverage / added CMake option `USE_LIBCXX` / fixed #10695 (#4611)

This commit is contained in:
Oliver Stöneberg 2022-12-07 09:20:09 +01:00 committed by GitHub
parent 89dba226dd
commit 8bb5ac0efd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 341 additions and 105 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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})

View File

@ -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<biguint>::max())
return std::numeric_limits<biguint>::max();
else
return static_cast<biguint>(doubleval);
else // cast to bigint to avoid UBSAN warning about negative double being out-of-range
return static_cast<biguint>(static_cast<bigint>(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<bigint>::max())
return std::numeric_limits<bigint>::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>(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)

View File

@ -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<unsigned long long>::min(), MathLib::toLongNumber(std::to_string(std::numeric_limits<unsigned long long>::min())));
ASSERT_EQUALS(std::numeric_limits<unsigned long long>::max(), MathLib::toLongNumber(std::to_string(std::numeric_limits<unsigned long long>::max())));
ASSERT_EQUALS(std::numeric_limits<long long>::min(), MathLib::toULongNumber(std::to_string(std::numeric_limits<long long>::min())));
ASSERT_EQUALS(std::numeric_limits<long long>::max(), MathLib::toULongNumber(std::to_string(std::numeric_limits<long long>::max())));
ASSERT_EQUALS(std::numeric_limits<unsigned long long>::min(), MathLib::toULongNumber(std::to_string(std::numeric_limits<unsigned long long>::min())));
ASSERT_EQUALS(std::numeric_limits<unsigned long long>::max(), MathLib::toULongNumber(std::to_string(std::numeric_limits<unsigned long long>::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<long long>::min(), MathLib::toULongNumber(std::to_string(std::numeric_limits<long long>::min())));
ASSERT_EQUALS(std::numeric_limits<long long>::max(), MathLib::toULongNumber(std::to_string(std::numeric_limits<long long>::max())));
ASSERT_EQUALS(std::numeric_limits<unsigned long long>::min(), MathLib::toULongNumber(std::to_string(std::numeric_limits<unsigned long long>::min())));
ASSERT_EQUALS(std::numeric_limits<unsigned long long>::max(), MathLib::toULongNumber(std::to_string(std::numeric_limits<unsigned long long>::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