#include "nmNumericDisplayFormat.h" #include #include #include #include namespace { /** @brief 删除尾零并缩短指数;只清理文字,不把小的非零值当作零。 */ QString trimNumber(QString sText) { const int nExponent = sText.indexOf(QLatin1Char('e')); QString sMantissa = nExponent < 0 ? sText : sText.left(nExponent); if(sMantissa.contains(QLatin1Char('.'))) { while(sMantissa.endsWith(QLatin1Char('0'))) sMantissa.chop(1); if(sMantissa.endsWith(QLatin1Char('.'))) sMantissa.chop(1); } if(sMantissa == QLatin1String("-0")) sMantissa = QLatin1String("0"); if(nExponent < 0) return sMantissa; const int nPower = sText.mid(nExponent + 1).toInt(); return sMantissa + QLatin1String("e") + (nPower >= 0 ? QLatin1String("+") : QLatin1String("")) + QString::number(nPower); } /** @brief 从默认显示逐级提升精度;最终使用十七位有效数字确保相邻 double 可区分。 */ QString formatWithExtraDigits(double dValue, int nExtraDigits) { if(!nmNumericDisplayFormat::isFinite(dValue)) return QString::fromLatin1("--"); if(dValue == 0.0) return QString::fromLatin1("0"); if(nExtraDigits >= 17) return nmNumericDisplayFormat::fullPrecision(dValue); const double dMagnitude = std::fabs(dValue); if(dMagnitude < 1e-4 || dMagnitude >= 1e8) { return trimNumber(QString::number(dValue, 'e', qMin(16, 3 + nExtraDigits))); } const int nDecimals = 4 + nExtraDigits; const int nIntegerDigits = static_cast(std::floor(std::log10(dMagnitude))) + 1; if(nIntegerDigits + nDecimals >= 17) return nmNumericDisplayFormat::fullPrecision(dValue); const QString sFixed = trimNumber(QString::number(dValue, 'f', nDecimals)); // 普通范围上沿四舍五入后也使用科学计数法,避免产生一串整数位。 if(std::fabs(sFixed.toDouble()) >= 1e8) { return trimNumber(QString::number(dValue, 'e', qMin(16, 3 + nExtraDigits))); } return sFixed; } } bool nmNumericDisplayFormat::isFinite(double dValue) { return dValue == dValue && dValue >= -DBL_MAX && dValue <= DBL_MAX; } QString nmNumericDisplayFormat::format(double dValue) { return formatWithExtraDigits(dValue, 0); } QString nmNumericDisplayFormat::fullPrecision(double dValue) { if(!isFinite(dValue)) return QString::fromLatin1("--"); if(dValue == 0.0) return QString::fromLatin1("0"); return trimNumber(QString::number(dValue, 'g', 17)); } QStringList nmNumericDisplayFormat::formatDistinct(const QVector& vecValues) { QVector vecExtraDigits(vecValues.size(), 0); QStringList listTexts; // 时间列表可能较长,用映射查找冲突,避免两两比较产生平方复杂度。 for(;;) { listTexts.clear(); QMap mapFirstValues; QSet setCollisions; for(int nIndex = 0; nIndex < vecValues.size(); ++nIndex) { const double dValue = vecValues[nIndex]; const QString sText = formatWithExtraDigits(dValue, vecExtraDigits[nIndex]); listTexts.append(sText); if(!isFinite(dValue)) continue; if(mapFirstValues.contains(sText) && mapFirstValues.value(sText) != dValue) { setCollisions.insert(sText); } else { mapFirstValues.insert(sText, dValue); } } if(setCollisions.isEmpty()) break; bool bIncreased = false; for(int nIndex = 0; nIndex < vecValues.size(); ++nIndex) { if(setCollisions.contains(listTexts[nIndex]) && vecExtraDigits[nIndex] < 17) { ++vecExtraDigits[nIndex]; bIncreased = true; } } if(!bIncreased) break; } return listTexts; }