0
QCOMPARE(
    std::numeric_limits<double>::infinity(),
    std::numeric_limits<double>::infinity());

fails with:

Compared doubles are not the same (fuzzy compare)
       Actual   (std::numeric_limits<double>::infinity()): inf
       Expected (std::numeric_limits<double>::infinity()): inf
       Loc: [...]

Question

Why does this fail? Is there a good work-around (other than using QVERIFY instead)?

UPDATE:

I mean a work-around from the perspective of a test-writer. It is desirable to provide a proper diagnostic with the actual value which is not infinity. Instead of

QCOMPARE(val, std::numeric_limits<double>::infinity());

one could

QVERIFY(val == std::numeric_limits<double>::infinity()))

but the value of val is not shown in the generated diagnostic message if the check fails. Looks like a major oversight from developers. So do I have to roll-out my own macro with exact comparison similar to QCOMPARE? Any recommendations here?

Also, it is clear that qFuzzyCompare does not support some corner-cases. But I hoped for a more in-depth explanation of why this is the case.

AMA
  • 4,114
  • 18
  • 32
  • Anything called "fuzzy compare" is inherently suspect. Apparently it's doing something other than comparing for exact equality. – Pete Becker Sep 29 '17 at 15:42
  • 1
    This smells like a good feature request to submit on [Qt's bugtracker](https://bugreports.qt.io/). – peppe Sep 29 '17 at 19:36
  • As a workaround, of course, you can c&p what QCOMPARE does for floating points (see [here](https://code.woboq.org/qt5/qtbase/src/testlib/qtestcase.cpp.html#_ZN5QTest8qCompareERKdS1_PKcS3_S3_i)), but turn that `qFuzzyCompare` in a more thorough check (e.g. via `fpclassify`). Which, by the way, opens the question on what you want to do with denormal numbers. – peppe Sep 29 '17 at 19:40

4 Answers4

3

QCOMPARE in the case of comparing floats and doubles, qFuzzyCompare() is used. (http://doc.qt.io/qt-5/qtest.html#QCOMPARE)

Based on (http://doc.qt.io/qt-5/qtglobal.html#qFuzzyCompare), Qt documentation it self says that comparing infinity will not work. - "Note that comparing values where either p1 or p2 is 0.0 will not work, nor does comparing values where one of the values is NaN or infinity".

My guess, why infinity can not be compared is, there is no specific value for infinity,I mean "infinity-1" is also infinity. Then obviously comparison is a question here.

Workaround:

If you want to compare infinity, Then use below function and compare the return Boolean values.

bool qIsInf(double d)

http://doc.qt.io/qt-5/qtglobal.html#qIsInf

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Pavan Chandaka
  • 11,671
  • 5
  • 26
  • 34
2

As there is still no working example, I post my own macro here:

#include <qtestcase.h>

#define COMPARE_DBL(actual, expected, epsilon) \
do {\
    if (!QTest::compare_helper((qAbs(actual - expected) <= epsilon), \
                               QString{"Compared values are not the same in respect to epsilon %1"} \
                                    .arg(epsilon).toLocal8Bit().constData(), \
                               QTest::toString(actual), \
                               QTest::toString(expected), \
                               #actual, #expected, __FILE__, __LINE__)) \
        return;\
} while (false)

It prints out an error message formatted by Qt and correctly interrupts the test case.

Thomas Klier
  • 449
  • 4
  • 16
0

I ended up writing a custom marco. It mimics QCOMPARE output and does not stick out.

Here it is (feel free to re-use):

inline void isEqualsExactly(
    const double actual,
    const double expected,
    const std::string& actualName,
    const std::string& expectedName)
{
    if(expected == actual)
        return;
    std::stringstream warn;
    const size_t w = std::max(actualName.length(), expectedName.length());
    warn << "Compared values are not the same" << std::endl;
    warn << "   Actual   " << std::setw(w) << std::setfill(' ') << actualName;
    warn << ": " << std::to_string(actual) << std::endl;
    warn << "   Expected " << std::setw(w) << std::setfill(' ') << expectedName;
    warn << ": " << std::to_string(expected);
    QFAIL(warn.str().c_str());
}

#define QCOMPARE_EXACT(actual, expected) \
    isEqualsExactly(actual, expected, #actual, #expected);

Usage:

const double val = 0;
const double infinity = std::numeric_limits<double>::infinity();
QCOMPARE_EXACT(val, infinity);

Output:

FAIL!  : TestName Compared values are not the same
   Actual        val: 0.000000
   Expected infinity: inf  
   Loc: [link to location in file]

P.S. I agree, this should be a feature request. The situation is ridiculous. If I will make one, I will update the post.

AMA
  • 4,114
  • 18
  • 32
  • You cannot use QFAIL within a function because it shall interrupt the test with a return call. In your implementation the test will continue in case of a fail. – Thomas Klier Jan 29 '21 at 11:44
0

Based on the approach by @thomas-klier, the following macro works for me:

#define COMPARE_DBL(actual, expected, epsilon) \
do {\
    if (!QTest::compare_helper( ((qAbs(actual - expected) <= epsilon) || (actual == expected)), \
        QString{"Compared values are not the same in respect to epsilon %1"} \
             .arg(epsilon).toLocal8Bit().constData(), \
        QTest::toString(actual), \
        QTest::toString(expected), \
        #actual, #expected, __FILE__, __LINE__)) \
        return;\
} while (false)
Jay-Pi
  • 343
  • 3
  • 13