mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-27 02:53:10 +01:00
fix(NumberParser): Rounds very large negative numbers to the incorrect values #3580
This commit is contained in:
@@ -33,7 +33,8 @@
|
|||||||
#if !defined(POCO_NO_LOCALE)
|
#if !defined(POCO_NO_LOCALE)
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#endif
|
#endif
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
#if defined(POCO_NOINTMAX)
|
#if defined(POCO_NOINTMAX)
|
||||||
typedef Poco::UInt64 uintmax_t;
|
typedef Poco::UInt64 uintmax_t;
|
||||||
typedef Poco::Int64 intmax_t;
|
typedef Poco::Int64 intmax_t;
|
||||||
@@ -188,26 +189,10 @@ bool strToInt(const char* pStr, I& outResult, short base, char thSep = ',')
|
|||||||
}
|
}
|
||||||
else if (*pStr == '+') ++pStr;
|
else if (*pStr == '+') ++pStr;
|
||||||
|
|
||||||
// all numbers are parsed as positive; the sign
|
// numbers are parsed as unsigned, for negative numbers the sign is applied after parsing
|
||||||
// for negative numbers is adjusted after parsing
|
// overflow is checked in every parse step
|
||||||
uintmax_t limitCheck = std::numeric_limits<I>::max();
|
uintmax_t limitCheck = negative ? -std::numeric_limits<I>::min() : std::numeric_limits<I>::max();
|
||||||
if (negative)
|
I result = 0;
|
||||||
{
|
|
||||||
poco_assert_dbg(std::numeric_limits<I>::is_signed);
|
|
||||||
// to cover the entire range, (-min > max) has to be
|
|
||||||
// taken into account;
|
|
||||||
// to avoid overflow for the largest int size,
|
|
||||||
// we resort to FPEnvironment::copySign() (ie. floating-point)
|
|
||||||
if (sizeof(I) == sizeof(intmax_t))
|
|
||||||
limitCheck = static_cast<uintmax_t>(FPEnvironment::copySign(static_cast<double>(std::numeric_limits<I>::min()), 1));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
intmax_t i = std::numeric_limits<I>::min();
|
|
||||||
limitCheck = -i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uintmax_t result = 0;
|
|
||||||
for (; *pStr != '\0'; ++pStr)
|
for (; *pStr != '\0'; ++pStr)
|
||||||
{
|
{
|
||||||
if (result > (limitCheck / base)) return false;
|
if (result > (limitCheck / base)) return false;
|
||||||
@@ -268,21 +253,9 @@ bool strToInt(const char* pStr, I& outResult, short base, char thSep = ',')
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (negative && (base == 10))
|
if (negative && (base == 10))
|
||||||
{
|
outResult = static_cast<I>(-result);
|
||||||
poco_assert_dbg(std::numeric_limits<I>::is_signed);
|
|
||||||
intmax_t i;
|
|
||||||
if (sizeof(I) == sizeof(intmax_t))
|
|
||||||
i = static_cast<intmax_t>(FPEnvironment::copySign(static_cast<double>(result), -1));
|
|
||||||
else
|
else
|
||||||
i = static_cast<intmax_t>(-result);
|
|
||||||
if (isIntOverflow<I>(i)) return false;
|
|
||||||
outResult = static_cast<I>(i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (isIntOverflow<I>(result)) return false;
|
|
||||||
outResult = static_cast<I>(result);
|
outResult = static_cast<I>(result);
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,6 +188,19 @@ void NumberParserTest::testLimits()
|
|||||||
assertTrue (testLowerLimit64<Int64>());
|
assertTrue (testLowerLimit64<Int64>());
|
||||||
assertTrue (testUpperLimit64<UInt64>());
|
assertTrue (testUpperLimit64<UInt64>());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Poco::Int64 val1, val2;
|
||||||
|
// smallest 64-bit int is actually -9223372036854775808
|
||||||
|
// but the sign and number are parsed as two tokens,
|
||||||
|
// resulting in compiler warning, for explanation see
|
||||||
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52661
|
||||||
|
NumberParser::tryParse64("-9223372036854775807", val1);
|
||||||
|
NumberParser::tryParse64("9223372036854775807", val2);
|
||||||
|
assertTrue (val1 == -9223372036854775807LL);
|
||||||
|
assertTrue (val2 == 9223372036854775807LL);
|
||||||
|
int i;
|
||||||
|
assertFalse (NumberParser::tryParse("-9223372036854775807", i));
|
||||||
|
assertFalse (NumberParser::tryParse("9223372036854775807", i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user