|
|
|
|
|
#include "nmNumericDisplayFormat.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include <QMap>
|
|
|
|
|
|
#include <QSet>
|
|
|
|
|
|
#include <float.h>
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
|
|
|
|
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<int>(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<double>& vecValues)
|
|
|
|
|
|
{
|
|
|
|
|
|
QVector<int> vecExtraDigits(vecValues.size(), 0);
|
|
|
|
|
|
QStringList listTexts;
|
|
|
|
|
|
// 时间列表可能较长,用映射查找冲突,避免两两比较产生平方复杂度。
|
|
|
|
|
|
for(;;) {
|
|
|
|
|
|
listTexts.clear();
|
|
|
|
|
|
QMap<QString, double> mapFirstValues;
|
|
|
|
|
|
QSet<QString> 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;
|
|
|
|
|
|
}
|