Repository navigation
Conversation
1b9ed64 to
6cdcde1
Compare
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
left a comment
There was a problem hiding this comment.
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 gives1e10000the valueinf.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.
| // 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); |
There was a problem hiding this comment.
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 falseInfinity 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.
There was a problem hiding this comment.
AI review
For a literal with an
l/Lsuffix,infis not the value the compiler uses:1e400Land1e500Lare different finitelong doublevalues. ValueFlow makes this a known value (vf_common.cppcallstoDoubleNumber()for each float literal and callssetKnown()), 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 falseInfinity is fine for literals without a suffix or with
f. Forlong doubleliterals out of the range ofdouble, it is better to give the token no known value, for example by checking for theLsuffix orstd::isinfinvf_common.cpp, than to give itinf. 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.
Trac: https://trac.cppcheck.net/ticket/12282
A float literal out of the range of
doublestops the analysis of the whole file:MathLib::toDoubleNumber()reads the literal with anistringstream. For a value out of range the stream stores the largest finite value and setsfailbit, which became the internal error. Compilers make such a literal infinity, so this does the same.Besides
long doubleconstants like the one in the ticket, a common trigger is glibc'smath.hwhen the GCC builtins are not used:HUGE_VAL/HUGE_VALFare then1e10000/1e10000f, so any file usingINFINITYorHUGE_VALhit this.Tests: new cases in
TestMathLib::toDoubleNumber(1e10000,1e10000f,1e10000L,1.5E+400,1.0e4931L); they fail without the change.