From 6cdcde1d139ac328bde0862079726017b6bfc95a Mon Sep 17 00:00:00 2001 From: Pieter Duchi Date: Wed, 21 Oct 2026 21:41:52 +0200 Subject: [PATCH 1/2] Fix #12282 (Internal error: fail to parse 1.0e4931L) A float literal out of the range of double, such as 1.0e4931L or glibc's HUGE_VAL for compilers other than GCC (1e10000), stopped the analysis of the whole file with Internal Error. MathLib::toDoubleNumber: conversion failed: 1e10000 The stream stores the largest finite value and fails on such a literal; compilers make it infinity, so do the same. --- lib/mathlib.cpp | 11 +++++++++-- test/testmathlib.cpp | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/mathlib.cpp b/lib/mathlib.cpp index c7c055899fd..b0f9d6b4d79 100644 --- a/lib/mathlib.cpp +++ b/lib/mathlib.cpp @@ -532,8 +532,15 @@ double MathLib::toDoubleNumber(const std::string &str, const Token * const tok) std::istringstream istr(str); istr.imbue(std::locale::classic()); double ret; - if (!(istr >> ret)) - throw InternalError(tok, "Internal Error. MathLib::toDoubleNumber: conversion failed: " + str); + if (!(istr >> ret)) { + // Out of the range of double, such as 1e10000 (glibc's HUGE_VAL for + // compilers other than GCC): the stream stores the largest finite + // value and fails, a compiler makes it infinity. + if (!isFloat(str) || std::fabs(ret) < std::numeric_limits::max()) + throw InternalError(tok, "Internal Error. MathLib::toDoubleNumber: conversion failed: " + str); + ret = std::copysign(std::numeric_limits::infinity(), ret); + istr.clear(); + } std::string s; if (istr >> s) { if (isDecimalFloat(str)) diff --git a/test/testmathlib.cpp b/test/testmathlib.cpp index 8cbe5b50019..85f4ba50a73 100644 --- a/test/testmathlib.cpp +++ b/test/testmathlib.cpp @@ -22,6 +22,7 @@ #include "token.h" #include "tokenlist.h" +#include #include #include #include @@ -677,6 +678,13 @@ class TestMathLib : public TestFixture { ASSERT_EQUALS_DOUBLE(static_cast('\200'), MathLib::toDoubleNumber("'\\200'"), 0.000001); ASSERT_EQUALS_DOUBLE(static_cast(L'A'), MathLib::toDoubleNumber("L'A'"), 0.000001); + // out of the range of double, like glibc's HUGE_VAL for compilers other than GCC + ASSERT_EQUALS(true, std::isinf(MathLib::toDoubleNumber("1e10000"))); + ASSERT_EQUALS(true, std::isinf(MathLib::toDoubleNumber("1e10000f"))); + ASSERT_EQUALS(true, std::isinf(MathLib::toDoubleNumber("1e10000L"))); + ASSERT_EQUALS(true, std::isinf(MathLib::toDoubleNumber("1.5E+400"))); + ASSERT_EQUALS(true, std::isinf(MathLib::toDoubleNumber("1.0e4931L"))); // #12282 + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("invalid"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: conversion failed: invalid"); // AppleClang before 18 reports a different error From 8e0d456f5990a04623c690fbfa521388e009f09c Mon Sep 17 00:00:00 2001 From: Pieter Duchi Date: Thu, 22 Oct 2026 08:20:27 +0200 Subject: [PATCH 2/2] Give long double literals out of the range of double no value Review of the previous commit: a long double literal larger than double, such as 1e400L, became a known value of infinity, so different literals compared equal and gave false positives: 1e400L / 1e500L duplicateExpression x == 1e400L || x == 1e500L knownConditionTrueFalse x > 1e400L && x < 1e500L incorrectLogicOperator Infinity is right for literals without suffix or with f (HUGE_VAL), not for long double. MathLib::isLongDoubleOutOfDoubleRange() tells them apart; ValueFlow gives such a literal no value and CheckCondition does not compare it. --- lib/checkcondition.cpp | 4 ++++ lib/mathlib.cpp | 7 +++++++ lib/mathlib.h | 2 ++ lib/vf_common.cpp | 16 ++++++++++------ test/testcondition.cpp | 12 ++++++++++++ test/testmathlib.cpp | 11 +++++++++++ test/testother.cpp | 12 ++++++++++++ 7 files changed, 58 insertions(+), 6 deletions(-) diff --git a/lib/checkcondition.cpp b/lib/checkcondition.cpp index 57c8412cdd9..ad547cf543e 100644 --- a/lib/checkcondition.cpp +++ b/lib/checkcondition.cpp @@ -1301,6 +1301,10 @@ void CheckConditionImpl::checkIncorrectLogicOperator() if (isfloat && (op1 == "==" || op1 == "!=" || op2 == "==" || op2 == "!=")) continue; + // no double value to compare (1e400L would become infinity) + if (isfloat && (MathLib::isLongDoubleOutOfDoubleRange(value1) || MathLib::isLongDoubleOutOfDoubleRange(value2))) + continue; + // the expr are not the token of the value but they provide better context const double d1 = isfloat ? MathLib::toDoubleNumber(value1) : 0; const double d2 = isfloat ? MathLib::toDoubleNumber(value2) : 0; diff --git a/lib/mathlib.cpp b/lib/mathlib.cpp index b0f9d6b4d79..9aba7118858 100644 --- a/lib/mathlib.cpp +++ b/lib/mathlib.cpp @@ -591,6 +591,13 @@ bool MathLib::isFloat(const std::string &str) return isDecimalFloat(str) || isFloatHex(str); } +bool MathLib::isLongDoubleOutOfDoubleRange(const std::string &str) +{ + if (str.empty() || (str.back() != 'l' && str.back() != 'L') || !isFloat(str)) + return false; + return std::isinf(toDoubleNumber(str)); +} + bool MathLib::isDecimalFloat(const std::string &str) { if (str.empty()) diff --git a/lib/mathlib.h b/lib/mathlib.h index 84338b5ce70..bfa1a5b877b 100644 --- a/lib/mathlib.h +++ b/lib/mathlib.h @@ -115,6 +115,8 @@ class CPPCHECKLIB MathLib { static bool isInt(const std::string & str); static bool isFloat(const std::string &str); + /** @brief a long double literal out of the range of double, such as 1e400L, which toDoubleNumber() makes infinity */ + static bool isLongDoubleOutOfDoubleRange(const std::string &str); static bool isDecimalFloat(const std::string &str); static bool isNegative(const std::string &str); static bool isPositive(const std::string &str); diff --git a/lib/vf_common.cpp b/lib/vf_common.cpp index b1cabcc9201..9b1d0387f38 100644 --- a/lib/vf_common.cpp +++ b/lib/vf_common.cpp @@ -139,12 +139,16 @@ namespace ValueFlow // Bad character literal } } else if (tok->isNumber() && MathLib::isFloat(tok->str())) { - Value value; - value.valueType = Value::ValueType::FLOAT; - value.floatValue = MathLib::toDoubleNumber(tok); - if (!tok->isTemplateArg()) - value.setKnown(); - setTokenValue(tok, std::move(value), settings); + // A long double literal out of the range of double (1e400L) has no + // double value: infinity would make different literals compare equal. + if (!MathLib::isLongDoubleOutOfDoubleRange(tok->str())) { + Value value; + value.valueType = Value::ValueType::FLOAT; + value.floatValue = MathLib::toDoubleNumber(tok); + if (!tok->isTemplateArg()) + value.setKnown(); + setTokenValue(tok, std::move(value), settings); + } } else if (tok->enumerator() && tok->enumerator()->value_known) { Value value(tok->enumerator()->value); if (!tok->isTemplateArg()) diff --git a/test/testcondition.cpp b/test/testcondition.cpp index b23bcc2e20b..abef2d95614 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -69,6 +69,7 @@ class TestCondition : public TestFixture { TEST_CASE(incorrectLogicOperator15); TEST_CASE(incorrectLogicOperator16); // #10070 TEST_CASE(incorrectLogicOperator17); + TEST_CASE(incorrectLogicOperator18); // #12282 TEST_CASE(secondAlwaysTrueFalseWhenFirstTrueError); TEST_CASE(incorrectLogicOp_condSwapping); TEST_CASE(testBug5895); @@ -1758,6 +1759,17 @@ class TestCondition : public TestFixture { ASSERT_EQUALS("", errout_str()); } + void incorrectLogicOperator18() { // #12282 + // long double literals out of the range of double are different values + check("int f(long double x) {\n" + " return x > 1e400L && x < 1e500L;\n" + "}\n" + "int g() {\n" + " return 1e400L < 1e500L;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + void incorrectLogicOperator17() { // #12471 check("struct R {\n" " void set() { i = 1; }\n" diff --git a/test/testmathlib.cpp b/test/testmathlib.cpp index 85f4ba50a73..800334926e5 100644 --- a/test/testmathlib.cpp +++ b/test/testmathlib.cpp @@ -45,6 +45,7 @@ class TestMathLib : public TestFixture { TEST_CASE(isnegative); TEST_CASE(ispositive); TEST_CASE(isFloat); + TEST_CASE(isLongDoubleOutOfDoubleRange); TEST_CASE(isDecimalFloat); TEST_CASE(isGreater); TEST_CASE(isGreaterEqual); @@ -599,6 +600,16 @@ class TestMathLib : public TestFixture { // TODO: test with 128-bit values } + void isLongDoubleOutOfDoubleRange() const { + ASSERT_EQUALS(true, MathLib::isLongDoubleOutOfDoubleRange("1e400L")); + ASSERT_EQUALS(true, MathLib::isLongDoubleOutOfDoubleRange("1.0e4931l")); + ASSERT_EQUALS(false, MathLib::isLongDoubleOutOfDoubleRange("1e300L")); + ASSERT_EQUALS(false, MathLib::isLongDoubleOutOfDoubleRange("1e10000")); + ASSERT_EQUALS(false, MathLib::isLongDoubleOutOfDoubleRange("1e10000f")); + ASSERT_EQUALS(false, MathLib::isLongDoubleOutOfDoubleRange("10L")); + ASSERT_EQUALS(false, MathLib::isLongDoubleOutOfDoubleRange("")); + } + void toDoubleNumber() const { // float values ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1."), 0.001); diff --git a/test/testother.cpp b/test/testother.cpp index f1d06182184..e8ba406c00c 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -204,6 +204,7 @@ class TestOther : public TestFixture { TEST_CASE(duplicateExpression20); TEST_CASE(duplicateExpression21); TEST_CASE(duplicateExpression22); + TEST_CASE(duplicateExpression23); // #12282 TEST_CASE(duplicateExpressionLoop); TEST_CASE(duplicateValueTernary); TEST_CASE(duplicateValueTernarySizeof); // #13773 @@ -8498,6 +8499,17 @@ class TestOther : public TestFixture { ASSERT_EQUALS("", errout_str()); } + void duplicateExpression23() { // #12282 + // long double literals out of the range of double are different values + check("long double f() {\n" + " return 1e400L / 1e500L;\n" + "}\n" + "int g(long double x) {\n" + " return x == 1e400L || x == 1e500L;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + void duplicateExpression22() { check("int f() {\n" // #14913 " return 0x1 | (0x2 | 0x4) | 0x1;\n"