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.
|
Test results for commit 8e0d456 (tools/test-my-pr.py, main compared to this PR): Test: http://ec2-16-170-140-253.eu-north-1.compute.amazonaws.com/pr-8952/ Posted automatically by the cppcheck PR test runner. +N: warnings only with this PR, -N: warnings only with main. The AI review is written by Claude and can be wrong. |
dmcppcheck
left a comment
There was a problem hiding this comment.
AI review
I found no new problems. The long double false positives from the earlier review are fixed in 8e0d456. Literals like 1e400L, -1e400L and 0x1p2000L now get no value, and incorrectLogicOperator skips them. Literals without a suffix or with f (HUGE_VAL/INFINITY style) become ±inf. That matches the compiler, so the new warnings on them are correct. Template arguments that hold such literals no longer cause an internal error, and none of my inputs gave a false positive or a crash.
One related limitation is not caused by this PR, and the base build has it too. A long double literal that underflows double, such as 1e-400L, still gets the value 0. So 1e-400L / 1e-500L gives duplicateExpression. isLongDoubleOutOfDoubleRange() only catches overflow. A follow-up could also treat a nonzero L literal that becomes 0 as out of range.
Testing
Build: I built cppcheck and testrunner in pr/, and cppcheck in base/. Both built without errors.
Unit tests: ./testrunner TestMathLib TestCondition TestOther TestValueFlow TestSimplifyTemplate TestTokenizer ran 1100 tests and none failed. That includes the new isLongDoubleOutOfDoubleRange, incorrectLogicOperator18 and duplicateExpression23. The PR does not touch any Python tests.
My own tests (pr/cppcheck --enable=all --disable=unusedFunction):
long doubleoverflow (a.cpp): I tried negative literals (-1e400L / -1e500L), hex floats (0x1p2000L / 0x1p3000L,x > 0x1p2000L && x < 0x1p3000L), lowercasel,1e400L * 2 > 1e500L,1e400L == 1e500L, a template argumentS<(1e400L < 1e500L)>, and a cast inside an array size.- PR: no warnings and no internal error.
- Base:
internalError.
HUGE_VAL/INFINITYdefined as1e10000/1e10000f(d.c):- Base:
internalError. - PR: the file is analysed. It reports
Condition 'y>0' is always falseforfloat y = -INFINITY;, which is correct.
- Base:
x == 1e10000 || x == 1e20000: the PR reportsknownConditionTrueFalse"same value". This is correct, because both literals are inf asdouble.- Not caused by this PR, base has it too:
long double r1(void) { return 1e-400L / 1e-500L; }givesduplicateExpressionin both base and PR (cppcheck -q --enable=style e.c).
Automated review of 8e0d456 by Claude. The pull request was built and tested locally.
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.