Important: Please read the Qt Code of Conduct -

QCOMPARE failing for doubles on Qt 5.5 only

  • Hi,

    Short version: is there any known reason QCOMPARE or qFuzzyCompare would have changed relative accuracy (aka fuzziness) for doubles in Qt 5.5?

    I maintain an open-source project that uses Qt (Bipolar). This project uses Travis CI and AppVeyor to build and test on Linux, OSX, and Windows, for a number of different Qt versions. All of these versions are building / passing tests as expected, except for those builds that use Qt 5.5.

    Current passing platforms:

    • Linux: Qt 5.1.1, 5.2.1, 5.3.2, 5.4.1 - all 64-bit, for both gcc and clang;
    • OSX: Qt 5.3.2 - 64-bit, for both gcc and clang.
    • Windows: Qt 5.3, 5.4 - 32-bit for both mingw and msvc2013; 64-bit for msvc2013-64.

    Current failing platforms:

    • Linux: Qt 5.5, both gcc and clang;
    • OSX: (not tested; I don't have Qt 5.5 readily available for OSX at the moment).
    • Windows: Qt 5.5, all of mingw-32, msvc2013-32 and msvc2013-64.

    The nature of the failure (on all failing tests) is like:

    FAIL!  : TestTrainingSession::toTCX(training-sessions-19401412) Compared doubles are not the same (fuzzy compare)
       Actual   (aDouble): 314.405
       Expected (bDouble): 314.405
       Loc: [../../bipolar/test/polar/v2/testtrainingsession.cpp(75)]

    So it would appear to be related to the relative accuracy qFuzzyCompare is using.

    An example of a build run in which everything passed, except for all of the Qt 5.5 builds:

    I'm more than happy to trace the issue further, before creating a formal bug report (if it turns out to be appropriate), etc But I just wanted to first check if this is a known / expected behaviour when upgrading to Qt 5.5. I've had a quick search through these forums, the Qt bug tracker, and the Qt 5.5 release notes, and see nothing relevant. It does seem strange that I'd be the only one seeing the issue, so it might be specific to something I'm doing.

    So, is anyone aware of any such issues with Qt 5.5? Or shall I dig deeper? :)



  • Ok, so I'm doing some digging, and it turns out that the fuzzy compare is doing the correct thing... on Qt 5.5 only the doubles differ by about 0.00008%, which is more than qFuzzyCompare allows. ie the allowance has not changed (between 4.4 and 4.5), but something else has, resulting in my application getting slightly different values... I'll need to dig deeper.

  • Lifetime Qt Champion

    Good luck :)

  • Thanks @mrjj :)

    I've found it!! The Qt 5.5 change was: Enhance precision of the FP conversion to strings in QVariant (8153386)

    This broke my tests, because my application code is using QVariant::toString to render XML content, then the unit test code is comparing the XML content to reference XML files. In this case, the reference files were generated using the pre-5.5 code, and so are actually less precise than the new XML content under test.

    This will be a slightly tricky one to solve in a way that supports all of Qt 5.x (desirable, but not crucial). I'll probably have to do an "is-float / is-double" check on the variant, and if so, invoke QString::number with an explicit precision that is consistent across versions. Either way, I'll have to update my XML test reference files, since they're obviously slightly lacking in precision as a result of the pre-Qt5.5 code used to generate them.

    Lots to ponder... :)

  • Lifetime Qt Champion

    I enjoyed your digging.
    Good work finding it and good info for others porting older apps.

  • I'm not usually one to drag up old topics, but just thought it was interesting enough to follow up with some new info here :)

    The same issue (or rather a new version of it) arose in Qt 5.7... the situation is pretty well summarised in the comments I added to the Bipolar project's code:

    // Qt 5.5 increased the accuracy of QVariant::toString output for floats and
    // doubles (see qtproject/qtbase@8153386), resulting in slightly different
    // output, and QCOMPARE unit test failures.
    // Qt 5.7 added QLocale::FloatingPointShortest (see qt/qtbase@726fed0), and
    // updated QVariant to use that (instead of the Qt 5.5 change above) when
    // converting floats and doubles to string, again resulting in slightly
    // different output, and QCOMPARE unit test failures.
    // So, QVariant floats and doubles convert (and compare) differently between
    // Qt 5.[0-4], 5.[5,6], and 5.7+.  Here we use the Qt 5.5 / 5.6 implementation
    // because its at least as accurate as 5.7+, and implementing a 5.7-compatible
    // fallback would be a major undertaking (needing to duplicate the third-party
    // double-conversion code Qt borrows from the V8 project).
    #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) && (QT_VERSION < QT_VERSION_CHECK(5, 7, 0))
        #define VARIANT_TO_STRING(v) v.toString()
    #else // Fallback implementation based closely on Qt 5.5's qvariant.cpp
        #ifndef DBL_MANT_DIG
        #define DBL_MANT_DIG  53
        #ifndef FLT_MANT_DIG
        #define FLT_MANT_DIG  24
        #define DBL_MAX_DIGITS10 (DBL_MANT_DIG * 30103) / 100000 + 2
        #define FLT_MAX_DIGITS10 (FLT_MANT_DIG * 30103) / 100000 + 2
        #define VARIANT_TO_STRING(v) \
            (static_cast<QMetaType::Type>(v.type()) == QMetaType::Double)      \
                ? QString::number(v.toDouble(), 'g', DBL_MAX_DIGITS10)         \
                : (static_cast<QMetaType::Type>(v.type()) == QMetaType::Float) \
                    ? QString::number(v.toFloat(), 'g', FLT_MAX_DIGITS10)      \
                    : v.toString()

    The Qt 5.7 change seems like a really good one, from a Qt-codebase maintenance perspective.

    Longer term, if this sort of change happens again (which is perfectly fine / valid for Qt to do - I'm not complaining at all), then I guess I'll make my code use an explicit float/double-to-string conversion function, and not rely on QVariant(float/double).toString in any version of Qt, just for this specific case.

    Anyway, it's been interesting :)


  • Lifetime Qt Champion

    Thank you for reporting back.
    This is very important info for people using this function as "stuff"
    will happen if you upgrade from older Qt.

Log in to reply