diff options
Diffstat (limited to 'src/corelib/kernel/qvariant.cpp')
-rw-r--r-- | src/corelib/kernel/qvariant.cpp | 213 |
1 files changed, 185 insertions, 28 deletions
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index f76201802b..f7b8fe5ca5 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -69,13 +69,18 @@ QT_BEGIN_NAMESPACE -#ifndef DBL_DIG -# define DBL_DIG 10 +#ifndef DBL_MANT_DIG +# define DBL_MANT_DIG 53 #endif -#ifndef FLT_DIG -# define FLT_DIG 6 +#ifndef FLT_MANT_DIG +# define FLT_MANT_DIG 24 #endif +const int log10_2_10000 = 30103; // log10(2) * 100000 +// same as C++11 std::numeric_limits<T>::max_digits10 +const int max_digits10_double = (DBL_MANT_DIG * log10_2_10000) / 100000 + 2; +const int max_digits10_float = (FLT_MANT_DIG * log10_2_10000) / 100000 + 2; + namespace { class HandlersManager { @@ -215,8 +220,9 @@ static qlonglong qConvertToNumber(const QVariant::Private *d, bool *ok) return qlonglong(qMetaTypeUNumber(d)); } - if (QMetaType::typeFlags(d->type) & QMetaType::IsEnumeration) { - switch (QMetaType::sizeOf(d->type)) { + QMetaType typeInfo(d->type); + if (typeInfo.flags() & QMetaType::IsEnumeration) { + switch (typeInfo.sizeOf()) { case 1: return d->is_shared ? *reinterpret_cast<signed char *>(d->data.shared->ptr) : d->data.sc; case 2: @@ -232,6 +238,26 @@ static qlonglong qConvertToNumber(const QVariant::Private *d, bool *ok) return Q_INT64_C(0); } +static qreal qConvertToRealNumber(const QVariant::Private *d, bool *ok) +{ + *ok = true; + switch (uint(d->type)) { + case QVariant::Double: + return qreal(d->data.d); + case QMetaType::Float: + return qreal(d->data.f); + case QVariant::ULongLong: + case QVariant::UInt: + case QMetaType::UChar: + case QMetaType::UShort: + case QMetaType::ULong: + return qreal(qMetaTypeUNumber(d)); + default: + // includes enum conversion as well as invalid types + return qreal(qConvertToNumber(d, ok)); + } +} + static qulonglong qConvertToUnsignedNumber(const QVariant::Private *d, bool *ok) { *ok = true; @@ -263,8 +289,9 @@ static qulonglong qConvertToUnsignedNumber(const QVariant::Private *d, bool *ok) return qMetaTypeUNumber(d); } - if (QMetaType::typeFlags(d->type) & QMetaType::IsEnumeration) { - switch (QMetaType::sizeOf(d->type)) { + QMetaType typeInfo(d->type); + if (typeInfo.flags() & QMetaType::IsEnumeration) { + switch (typeInfo.sizeOf()) { case 1: return d->is_shared ? *reinterpret_cast<uchar *>(d->data.shared->ptr) : d->data.uc; case 2: @@ -355,10 +382,10 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok) *str = QString::number(qMetaTypeUNumber(d)); break; case QMetaType::Float: - *str = QString::number(d->data.f, 'g', FLT_DIG); + *str = QString::number(d->data.f, 'g', max_digits10_float); break; case QVariant::Double: - *str = QString::number(d->data.d, 'g', DBL_DIG); + *str = QString::number(d->data.d, 'g', max_digits10_double); break; #if !defined(QT_NO_DATESTRING) case QVariant::Date: @@ -535,10 +562,10 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok) *ba = v_cast<QString>(d)->toUtf8(); break; case QVariant::Double: - *ba = QByteArray::number(d->data.d, 'g', DBL_DIG); + *ba = QByteArray::number(d->data.d, 'g', max_digits10_double); break; case QMetaType::Float: - *ba = QByteArray::number(d->data.f, 'g', FLT_DIG); + *ba = QByteArray::number(d->data.f, 'g', max_digits10_float); break; case QMetaType::Char: case QMetaType::SChar: @@ -3133,8 +3160,22 @@ bool QVariant::convert(const int type, void *ptr) const static bool qIsNumericType(uint tp) { - return (tp >= QVariant::Bool && tp <= QVariant::Double) - || (tp >= QMetaType::Long && tp <= QMetaType::Float); + static const qulonglong numericTypeBits = + Q_UINT64_C(1) << QMetaType::Bool | + Q_UINT64_C(1) << QMetaType::Double | + Q_UINT64_C(1) << QMetaType::Float | + Q_UINT64_C(1) << QMetaType::Char | + Q_UINT64_C(1) << QMetaType::SChar | + Q_UINT64_C(1) << QMetaType::UChar | + Q_UINT64_C(1) << QMetaType::Short | + Q_UINT64_C(1) << QMetaType::UShort | + Q_UINT64_C(1) << QMetaType::Int | + Q_UINT64_C(1) << QMetaType::UInt | + Q_UINT64_C(1) << QMetaType::Long | + Q_UINT64_C(1) << QMetaType::ULong | + Q_UINT64_C(1) << QMetaType::LongLong | + Q_UINT64_C(1) << QMetaType::ULongLong; + return tp < (CHAR_BIT * sizeof numericTypeBits) ? numericTypeBits & (Q_UINT64_C(1) << tp) : false; } static bool qIsFloatingPoint(uint tp) @@ -3142,26 +3183,137 @@ static bool qIsFloatingPoint(uint tp) return tp == QVariant::Double || tp == QMetaType::Float; } +static int normalizeLowerRanks(uint tp) +{ + static const qulonglong numericTypeBits = + Q_UINT64_C(1) << QMetaType::Bool | + Q_UINT64_C(1) << QMetaType::Char | + Q_UINT64_C(1) << QMetaType::SChar | + Q_UINT64_C(1) << QMetaType::UChar | + Q_UINT64_C(1) << QMetaType::Short | + Q_UINT64_C(1) << QMetaType::UShort; + return numericTypeBits & (Q_UINT64_C(1) << tp) ? QVariant::Int : tp; +} + +static int normalizeLong(uint tp) +{ + const uint IntType = sizeof(long) == sizeof(int) ? QVariant::Int : QVariant::LongLong; + const uint UIntType = sizeof(ulong) == sizeof(uint) ? QVariant::UInt : QVariant::ULongLong; + return tp == QMetaType::Long ? IntType : + tp == QMetaType::ULong ? UIntType : tp; +} + +static int numericTypePromotion(uint t1, uint t2) +{ + Q_ASSERT(qIsNumericType(t1)); + Q_ASSERT(qIsNumericType(t2)); + + // C++ integral ranks: (4.13 Integer conversion rank [conv.rank]) + // bool < signed char < short < int < long < long long + // unsigneds have the same rank as their signed counterparts + // C++ integral promotion rules (4.5 Integral Promotions [conv.prom]) + // - any type with rank less than int can be converted to int or unsigned int + // 5 Expressions [expr] paragraph 9: + // - if either operand is double, the other shall be converted to double + // - " " float, " " " float + // - if both operands have the same type, no further conversion is needed. + // - if both are signed or if both are unsigned, convert to the one with highest rank + // - if the unsigned has higher or same rank, convert the signed to the unsigned one + // - if the signed can represent all values of the unsigned, convert to the signed + // - otherwise, convert to the unsigned corresponding to the rank of the signed + + // floating point: we deviate from the C++ standard by always using qreal + if (qIsFloatingPoint(t1) || qIsFloatingPoint(t2)) + return QMetaType::QReal; + + // integral rules: + // for all platforms we support, int can always hold the values of lower-ranked types + t1 = normalizeLowerRanks(t1); + t2 = normalizeLowerRanks(t2); + + // normalize long / ulong: in all platforms we run, they're either the same as int or as long long + t1 = normalizeLong(t1); + t2 = normalizeLong(t2); + + // implement the other rules + // the four possibilities are Int, UInt, LongLong and ULongLong + // if any of the two is ULongLong, then it wins (highest rank, unsigned) + // otherwise, if one of the two is LongLong, then the other is either LongLong too or lower-ranked + // otherwise, if one of the two is UInt, then the other is either UInt too or Int + if (t1 == QVariant::ULongLong || t2 == QVariant::ULongLong) + return QVariant::ULongLong; + if (t1 == QVariant::LongLong || t2 == QVariant::LongLong) + return QVariant::LongLong; + if (t1 == QVariant::UInt || t2 == QVariant::UInt) + return QVariant::UInt; + return QVariant::Int; +} + +static int integralCompare(uint promotedType, const QVariant::Private *d1, const QVariant::Private *d2) +{ + // use toLongLong to retrieve the data, it gets us all the bits + bool ok; + qlonglong l1 = qConvertToNumber(d1, &ok); + Q_ASSERT(ok); + + qlonglong l2 = qConvertToNumber(d2, &ok); + Q_ASSERT(ok); + + if (promotedType == QVariant::Int) + return int(l1) < int(l2) ? -1 : int(l1) == int(l2) ? 0 : 1; + if (promotedType == QVariant::UInt) + return uint(l1) < uint(l2) ? -1 : uint(l1) == uint(l2) ? 0 : 1; + if (promotedType == QVariant::LongLong) + return l1 < l2 ? -1 : l1 == l2 ? 0 : 1; + if (promotedType == QVariant::ULongLong) + return qulonglong(l1) < qulonglong(l2) ? -1 : qulonglong(l1) == qulonglong(l2) ? 0 : 1; + + Q_UNREACHABLE(); + return 0; +} + +static int numericCompare(const QVariant::Private *d1, const QVariant::Private *d2) +{ + uint promotedType = numericTypePromotion(d1->type, d2->type); + if (promotedType != QMetaType::QReal) + return integralCompare(promotedType, d1, d2); + + // qreal comparisons + bool ok; + qreal r1 = qConvertToRealNumber(d1, &ok); + Q_ASSERT(ok); + qreal r2 = qConvertToRealNumber(d2, &ok); + Q_ASSERT(ok); + if (qFuzzyCompare(r1, r2)) + return 0; + return r1 < r2 ? -1 : 1; +} + /*! \internal */ bool QVariant::cmp(const QVariant &v) const { + // try numerics first, with C++ type promotion rules (no conversion) + if (qIsNumericType(d.type) && qIsNumericType(v.d.type)) + return numericCompare(&d, &v.d) == 0; + QVariant v1 = *this; QVariant v2 = v; if (d.type != v2.d.type) { - if (qIsNumericType(d.type) && qIsNumericType(v.d.type)) { - if (qIsFloatingPoint(d.type) || qIsFloatingPoint(v.d.type)) - return qFuzzyCompare(toReal(), v.toReal()); - else - return toLongLong() == v.toLongLong(); + if (v2.canConvert(v1.d.type)) { + if (!v2.convert(v1.d.type)) + return false; + } else { + // try the opposite conversion, it might work + qSwap(v1, v2); + if (!v2.convert(v1.d.type)) + return false; } - if (!v2.canConvert(v1.d.type) || !v2.convert(v1.d.type)) - return false; } if (v1.d.type >= QMetaType::User) { int result; - if (QMetaType::compare(QT_PREPEND_NAMESPACE(constData(v1.d)), QT_PREPEND_NAMESPACE(constData(v2.d)), v1.d.type, &result)) + if (QMetaType::equals(QT_PREPEND_NAMESPACE(constData(v1.d)), QT_PREPEND_NAMESPACE(constData(v2.d)), v1.d.type, &result)) return result == 0; } return handlerManager[v1.d.type]->compare(&v1.d, &v2.d); @@ -3172,10 +3324,17 @@ bool QVariant::cmp(const QVariant &v) const */ int QVariant::compare(const QVariant &v) const { + // try numerics first, with C++ type promotion rules (no conversion) + if (qIsNumericType(d.type) && qIsNumericType(v.d.type)) + return numericCompare(&d, &v.d); + + // check for equality next, as more types implement operator== than operator< if (cmp(v)) return 0; + QVariant v1 = *this; QVariant v2 = v; + if (v1.d.type != v2.d.type) { // if both types differ, try to convert if (v2.canConvert(v1.d.type)) { @@ -3197,18 +3356,16 @@ int QVariant::compare(const QVariant &v) const } return r; } + + // did we end up with two numerics? If so, restart + if (qIsNumericType(v1.d.type) && qIsNumericType(v2.d.type)) + return v1.compare(v2); } if (v1.d.type >= QMetaType::User) { int result; if (QMetaType::compare(QT_PREPEND_NAMESPACE(constData(d)), QT_PREPEND_NAMESPACE(constData(v2.d)), d.type, &result)) return result; } - if (qIsNumericType(v1.d.type)) { - if (qIsFloatingPoint(v1.d.type)) - return v1.toReal() < v2.toReal() ? -1 : 1; - else - return v1.toLongLong() < v2.toLongLong() ? -1 : 1; - } switch (v1.d.type) { case QVariant::Date: return v1.toDate() < v2.toDate() ? -1 : 1; |