From 9e1a2b4603c9791081481a34576970e1e6c85ef5 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Fri, 30 Jul 2021 15:26:28 +0200 Subject: [PATCH] We do in fact support 'F' format for floating-point values Update docs and add tests. [ChangeLog][QtCore] Documented existing support for 'F' format when converting floating-point numbers to strings in QLocale::toString(), hence equally for QString's floating-point formatting. Previously it was supported but the documentation neglected to mention it; it only differs from 'f' for infinities and NaN. Change-Id: Ic946c0f7b9e86fdf512daa3124bea57fc664b34b Reviewed-by: Qt CI Bot Reviewed-by: Sona Kurazyan --- src/corelib/text/qlocale.cpp | 22 +++-- .../auto/corelib/text/qlocale/tst_qlocale.cpp | 96 +++++++++++++++++++ .../auto/corelib/text/qstring/tst_qstring.cpp | 7 +- 3 files changed, 114 insertions(+), 11 deletions(-) diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index 926fa50bd8..69249012f5 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -2646,22 +2646,24 @@ static bool qIsUpper(char c) \row \li \c 'e' \li format as [-]9.9e[+|-]999 \row \li \c 'E' \li format as [-]9.9E[+|-]999 \row \li \c 'f' \li format as [-]9.9 + \row \li \c 'F' \li same as \c 'f' except for INF and NAN (see below) \row \li \c 'g' \li use \c 'e' or \c 'f' format, whichever is more concise - \row \li \c 'G' \li use \c 'E' or \c 'f' format, whichever is more concise + \row \li \c 'G' \li use \c 'E' or \c 'F' format, whichever is more concise \endtable - For the \c 'e', \c 'E', and \c 'f' formats, the \a precision represents the - number of digits \e after the decimal point. For the \c 'g' and \c 'G' - formats, the \a precision represents the maximum number of significant - digits (trailing zeroes are omitted). The special \a precision value - QLocale::FloatingPointShortest selects the shortest representation that, - when read as a number, gets back the original floating-point value. Aside - from that, any negative \a precision is ignored in favor of the default, 6. + For the \c 'e', \c 'E', \c 'f' and \c 'F' formats, the \a precision + represents the number of digits \e after the decimal point. For the \c 'g' + and \c 'G' formats, the \a precision represents the maximum number of + significant digits (trailing zeroes are omitted). The special \a precision + value QLocale::FloatingPointShortest selects the shortest representation + that, when read as a number, gets back the original floating-point + value. Aside from that, any negative \a precision is ignored in favor of the + default, 6. For the \c 'e', \c 'f' and \c 'g' formats, positive infinity is represented as "inf", negative infinity as "-inf" and floating-point NaN (not-a-number) - values are represented as "nan". For the \c 'E' and \c 'G' formats, "INF" - and "NAN" are used instead. This does not vary with locale. + values are represented as "nan". For the \c 'E', \c 'F' and \c 'G' formats, + "INF" and "NAN" are used instead. This does not vary with locale. \sa toDouble(), numberOptions(), exponential(), decimalPoint(), zeroDigit(), positiveSign(), percent(), toCurrencyString(), formattedDataSize(), diff --git a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp index a7eb29d2d1..09f034cb0e 100644 --- a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp @@ -90,6 +90,7 @@ private slots: void long_long_conversion_data(); void long_long_conversion(); void long_long_conversion_extra(); + void infNaN(); void fpExceptions(); void negativeZero_data(); void negativeZero(); @@ -1408,6 +1409,101 @@ void tst_QLocale::long_long_conversion_extra() QCOMPARE(l.toString((qulonglong)12345), QString("12,345")); } +void tst_QLocale::infNaN() +{ + // TODO: QTBUG-95460 -- could support localized forms of inf/NaN + const QLocale c(QLocale::C); + QCOMPARE(c.toString(qQNaN()), u"nan"); + QCOMPARE(c.toString(qQNaN(), 'e'), u"nan"); + QCOMPARE(c.toString(qQNaN(), 'f'), u"nan"); + QCOMPARE(c.toString(qQNaN(), 'g'), u"nan"); + QCOMPARE(c.toString(qQNaN(), 'E'), u"NAN"); + QCOMPARE(c.toString(qQNaN(), 'F'), u"NAN"); + QCOMPARE(c.toString(qQNaN(), 'G'), u"NAN"); + + QCOMPARE(c.toString(qInf()), u"inf"); + QCOMPARE(c.toString(qInf(), 'e'), u"inf"); + QCOMPARE(c.toString(qInf(), 'f'), u"inf"); + QCOMPARE(c.toString(qInf(), 'g'), u"inf"); + QCOMPARE(c.toString(qInf(), 'E'), u"INF"); + QCOMPARE(c.toString(qInf(), 'F'), u"INF"); + QCOMPARE(c.toString(qInf(), 'G'), u"INF"); + + // Precision is ignored for inf and NaN: + QCOMPARE(c.toString(qQNaN(), 'g', 42), u"nan"); + QCOMPARE(c.toString(qQNaN(), 'G', 42), u"NAN"); + QCOMPARE(c.toString(qInf(), 'g', 42), u"inf"); + QCOMPARE(c.toString(qInf(), 'G', 42), u"INF"); + + // Case is ignored when parsing inf and NaN: + bool ok = false; + QCOMPARE(c.toDouble("inf", &ok), qInf()); + QVERIFY(ok); + QCOMPARE(c.toDouble("INF", &ok), qInf()); + QVERIFY(ok); + QCOMPARE(c.toDouble("Inf", &ok), qInf()); + QVERIFY(ok); + QCOMPARE(c.toDouble("+inf", &ok), qInf()); + QVERIFY(ok); + QCOMPARE(c.toDouble("+INF", &ok), qInf()); + QVERIFY(ok); + QCOMPARE(c.toDouble("+inF", &ok), qInf()); + QVERIFY(ok); + QCOMPARE(c.toDouble("-inf", &ok), -qInf()); + QVERIFY(ok); + QCOMPARE(c.toDouble("-INF", &ok), -qInf()); + QVERIFY(ok); + QCOMPARE(c.toDouble("-iNf", &ok), -qInf()); + QVERIFY(ok); + QCOMPARE(c.toDouble("nan", &ok), qQNaN()); + QVERIFY(ok); + QCOMPARE(c.toDouble("NaN", &ok), qQNaN()); + QVERIFY(ok); + QCOMPARE(c.toDouble("NAN", &ok), qQNaN()); + QVERIFY(ok); + QCOMPARE(c.toDouble("nAn", &ok), qQNaN()); + QVERIFY(ok); + // Sign is invalid for NaN: + QCOMPARE(c.toDouble("-nan", &ok), 0.0); + QVERIFY(!ok); + QCOMPARE(c.toDouble("+nan", &ok), 0.0); + QVERIFY(!ok); + + + // Case is ignored when parsing inf and NaN: + QCOMPARE(c.toFloat("inf", &ok), float(qInf())); + QVERIFY(ok); + QCOMPARE(c.toFloat("INF", &ok), float(qInf())); + QVERIFY(ok); + QCOMPARE(c.toFloat("Inf", &ok), float(qInf())); + QVERIFY(ok); + QCOMPARE(c.toFloat("+inf", &ok), float(qInf())); + QVERIFY(ok); + QCOMPARE(c.toFloat("+INF", &ok), float(qInf())); + QVERIFY(ok); + QCOMPARE(c.toFloat("+inF", &ok), float(qInf())); + QVERIFY(ok); + QCOMPARE(c.toFloat("-inf", &ok), -float(qInf())); + QVERIFY(ok); + QCOMPARE(c.toFloat("-INF", &ok), -float(qInf())); + QVERIFY(ok); + QCOMPARE(c.toFloat("-iNf", &ok), -float(qInf())); + QVERIFY(ok); + QCOMPARE(c.toFloat("nan", &ok), float(qQNaN())); + QVERIFY(ok); + QCOMPARE(c.toFloat("NaN", &ok), float(qQNaN())); + QVERIFY(ok); + QCOMPARE(c.toFloat("NAN", &ok), float(qQNaN())); + QVERIFY(ok); + QCOMPARE(c.toFloat("nAn", &ok), float(qQNaN())); + QVERIFY(ok); + // Sign is invalid for NaN: + QCOMPARE(c.toFloat("-nan", &ok), 0.0f); + QVERIFY(!ok); + QCOMPARE(c.toFloat("+nan", &ok), 0.0f); + QVERIFY(!ok); +} + void tst_QLocale::fpExceptions() { // Check that double-to-string conversion doesn't throw floating point diff --git a/tests/auto/corelib/text/qstring/tst_qstring.cpp b/tests/auto/corelib/text/qstring/tst_qstring.cpp index 7743c13dc0..e42ca7d199 100644 --- a/tests/auto/corelib/text/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/text/qstring/tst_qstring.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Copyright (C) 2020 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -6430,6 +6430,11 @@ void tst_QString::nanAndInf() QCOMPARE(form.arg(-qInf(), 6, 'f', 3, '0'), u"00-inf"); QCOMPARE(form.arg(-qInf(), -6, 'f', 3, '0'), u"-inf00"); QCOMPARE(form.arg(qQNaN(), -5, 'f', 3, '0'), u"nan00"); + QCOMPARE(form.arg(qInf(), 5, 'F', 3, '0'), u"00INF"); + QCOMPARE(form.arg(qInf(), -5, 'F', 3, '0'), u"INF00"); + QCOMPARE(form.arg(-qInf(), 6, 'F', 3, '0'), u"00-INF"); + QCOMPARE(form.arg(-qInf(), -6, 'F', 3, '0'), u"-INF00"); + QCOMPARE(form.arg(qQNaN(), -5, 'F', 3, '0'), u"NAN00"); QCOMPARE(form.arg(qInf(), 5, 'e', 3, '0'), u"00inf"); QCOMPARE(form.arg(qInf(), -5, 'e', 3, '0'), u"inf00"); QCOMPARE(form.arg(-qInf(), 6, 'e', 3, '0'), u"00-inf");