Skip to content

Fix #12282 (Internal error: fail to parse 1.0e4931L) - #8952

Open
pduchi wants to merge 2 commits into
cppcheck-opensource:mainfrom
pduchi:mathlib-out-of-range-float-literal
Open

pduchi wants to merge 2 commits into
cppcheck-opensource:mainfrom
pduchi:mathlib-out-of-range-float-literal

Conversation

@pduchi

@pduchi pduchi commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Trac: https://trac.cppcheck.net/ticket/12282

A float literal out of the range of double stops the analysis of the whole file:

Internal Error. MathLib::toDoubleNumber: conversion failed: 1.0e4931L

MathLib::toDoubleNumber() reads the literal with an istringstream. For a value out of range the stream stores the largest finite value and sets failbit, which became the internal error. Compilers make such a literal infinity, so this does the same.

Besides long double constants like the one in the ticket, a common trigger is glibc's math.h when the GCC builtins are not used: HUGE_VAL/HUGE_VALF are then 1e10000/1e10000f, so any file using INFINITY or HUGE_VAL hit this.

Tests: new cases in TestMathLib::toDoubleNumber (1e10000, 1e10000f, 1e10000L, 1.5E+400, 1.0e4931L); they fail without the change.

@pduchi
pduchi force-pushed the mathlib-out-of-range-float-literal branch from 1b9ed64 to 6cdcde1 Compare October 21, 2026 19:53
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.

@dmcppcheck dmcppcheck left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

The internal error is gone, but the PR adds false positives for long double literals, which the ticket is about. Any L literal above DBL_MAX (such as 1e400L or 1.0e4931L) now becomes a known float value of +inf. So different, in-range long double constants compare equal, and the checkers that compare values report wrong results: duplicateExpression, knownConditionTrueFalse and incorrectLogicOperator. Before the PR these files stopped with an internal error. Now they get warnings that look believable but are wrong. For literals without a suffix, or with f, infinity is correct, as is the HUGE_VAL case. I found no other problems.

Testing

I built cppcheck and testrunner in pr/ and cppcheck in base/.

  • ./testrunner TestMathLib: all 30 tests pass.
  • With the PR, HUGE_VAL-style literals (1e10000, -1e10000f, 1.0e4931L) no longer cause an internal error. ValueFlow gives 1e10000 the value inf.0.

False positives with long double literals (scratch/c.c):

long double r1(void) { return 1e400L / 1e500L; }
int r2(long double x) { return x > 1e400L && x < 1e500L; }
int r3(long double x) { return x == 1e400L || x == 1e500L; }
int r4(void) { return 1e400L < 1e500L; }

pr/cppcheck --enable=all --disable=unusedFunction c.c:

c.c:2:43: warning: Logical conjunction always evaluates to false: x > 1e400L && x < 1e500L. [incorrectLogicOperator]
c.c:1:38: style: Same expression on both sides of '/'. [duplicateExpression]
c.c:3:44: style: Same expression on both sides of '||' because 'x==1e400L' and 'x==1e500L' represent the same value. [knownConditionTrueFalse]
c.c:4:30: style: The comparison '1e400L < 1e500L' is always false. [knownConditionTrueFalse]

All four are wrong: on x86-64, long double reaches about 1.19e4932, so these are different finite values, and r4 is always true. The base build only reports Internal Error. MathLib::toDoubleNumber: conversion failed: 1e400L.


Automated review of 6cdcde1 by Claude. The pull request was built and tested locally.

Comment thread lib/mathlib.cpp
// value and fails, a compiler makes it infinity.
if (!isFloat(str) || std::fabs(ret) < std::numeric_limits<double>::max())
throw InternalError(tok, "Internal Error. MathLib::toDoubleNumber: conversion failed: " + str);
ret = std::copysign(std::numeric_limits<double>::infinity(), ret);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

For a literal with an l/L suffix, inf is not the value the compiler uses: 1e400L and 1e500L are different finite long double values. ValueFlow makes this a known value (vf_common.cpp calls toDoubleNumber() for each float literal and calls setKnown()), so different literals now compare equal and checkers give false positives:

long double r1(void) { return 1e400L / 1e500L; }            // duplicateExpression
int r2(long double x) { return x > 1e400L && x < 1e500L; } // incorrectLogicOperator
int r4(void) { return 1e400L < 1e500L; }                   // knownConditionTrueFalse: always false

Infinity is fine for literals without a suffix or with f. For long double literals out of the range of double, it is better to give the token no known value, for example by checking for the L suffix or std::isinf in vf_common.cpp, than to give it inf. Please also add a regression test for this case, for example in TestCondition or TestOther.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

For a literal with an l/L suffix, inf is not the value the compiler uses: 1e400L and 1e500L are different finite long double values. ValueFlow makes this a known value (vf_common.cpp calls toDoubleNumber() for each float literal and calls setKnown()), so different literals now compare equal and checkers give false positives:

long double r1(void) { return 1e400L / 1e500L; }            // duplicateExpression
int r2(long double x) { return x > 1e400L && x < 1e500L; } // incorrectLogicOperator
int r4(void) { return 1e400L < 1e500L; }                   // knownConditionTrueFalse: always false

Infinity is fine for literals without a suffix or with f. For long double literals out of the range of double, it is better to give the token no known value, for example by checking for the L suffix or std::isinf in vf_common.cpp, than to give it inf. Please also add a regression test for this case, for example in TestCondition or TestOther.

Thanks, you're right: inf is only correct without a suffix or with f. Fixed in 8e0d456: MathLib::isLongDoubleOutOfDoubleRange() detects L literals that don't fit a double; ValueFlow gives them no value, and CheckCondition (which converts the literals itself for incorrectLogicOperator) skips them. Your examples are added as regression tests (TestCondition::incorrectLogicOperator18, TestOther::duplicateExpression23); they failed before this commit.

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants