Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:epopov:branches:openSUSE:Factory
kcalc
2001-better-numeral-system-view.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 2001-better-numeral-system-view.patch of Package kcalc
diff --git a/kcalc.cpp b/kcalc.cpp index ad83e0135df70ce2f951e93e5098e1de86987914..75c58574cd4adabcbc2dacee47ce2600d201256a 100644 --- a/kcalc.cpp +++ b/kcalc.cpp @@ -1419,9 +1419,10 @@ void KCalculator::slotInputChanged() } else if (calculation_failure_) { switch (calculation_result_code_) { case CalcEngine::ResultCode::MISIING_RIGHT_UNARY_ARG: + break; case CalcEngine::ResultCode::MISIING_RIGHT_BINARY_ARG: if (calc_display->text().isEmpty()) { - numeralSystemView->clearNumber(); + numeralSystemView->setNumber(KNumber::NaN); } break; case CalcEngine::ResultCode::MATH_ERROR: @@ -2029,7 +2030,7 @@ void KCalculator::slotSetNumeralMode() //------------------------------------------------------------------------------ void KCalculator::slotBaseModeAmountChanged(const KNumber &number) { - numeralSystemView->setNumber(number.toUint64(), base_mode_); + numeralSystemView->setNumber(number, base_mode_); } //------------------------------------------------------------------------------ @@ -2039,7 +2040,7 @@ void KCalculator::slotBaseModeAmountChanged(const KNumber &number) void KCalculator::slotClearBaseModeAmount() { if (m_parsingResult != KCalcParser::ParsingResult::SUCCESS_SINGLE_KNUMBER) { - numeralSystemView->clearNumber(); + numeralSystemView->setNumber(KNumber::NaN); } } diff --git a/kcalc_numeralsystem_view.cpp b/kcalc_numeralsystem_view.cpp index 24d029fd34cdc905e329743dfb30d239e844d2ca..9d3eb0784e438d9e4b016e8db88666d53766466b 100644 --- a/kcalc_numeralsystem_view.cpp +++ b/kcalc_numeralsystem_view.cpp @@ -14,6 +14,8 @@ #include <KLocalizedString> #include <KSqueezedTextLabel> +#include "kcalc_settings.h" + // KCalcNumeralSystemLabel KCalcNumeralSystemLabel::KCalcNumeralSystemLabel(QWidget *parent) @@ -42,10 +44,21 @@ KCalcNumeralSystemLabel::KCalcNumeralSystemLabel(QWidget *parent) updateAlignment(); } -void KCalcNumeralSystemLabel::setNumber(quint64 number, int base) +void KCalcNumeralSystemLabel::setNumber(const KNumber &number, int base) { - m_textLabel->setText(QString::number(number, base).toUpper()); - m_textLabel->setAccessibleName(m_textLabel->fullText()); + QString text; + if (KCalcSettings::twosComplement() && base != 10 && number.type() != KNumber::TYPE_ERROR) { + text = QString::number(number.integerPart().toUint64(), base).toUpper(); + } else { + int fixedPrecision = -1; + if (KCalcSettings::fixed()) { + fixedPrecision = KCalcSettings::fixedPrecision(); + } + text = number.toStringInBase(base, KCalcSettings::precision(), fixedPrecision); + } + + m_textLabel->setText(text); + m_textLabel->setAccessibleName(text); m_numeralSystemLabel->setText(QStringLiteral("<small><b>%1</b></small>").arg(base)); } @@ -83,9 +96,15 @@ KCalcNumeralSystemView::KCalcNumeralSystemView(QWidget *parent) setLayout(layout); updateLabels(); + + connect(KCalcSettings::self(), &KCalcSettings::configChanged, this, [this]() { + if (m_number.type() != KNumber::TYPE_ERROR) { + updateLabels(); + } + }); } -void KCalcNumeralSystemView::setNumber(quint64 number, int base) +void KCalcNumeralSystemView::setNumber(const KNumber &number, int base) { if (m_number == number && m_base == base) { return; @@ -97,42 +116,30 @@ void KCalcNumeralSystemView::setNumber(quint64 number, int base) updateLabels(); } -void KCalcNumeralSystemView::clearNumber() -{ - if (!m_number) { - return; - } - - m_number.reset(); - - updateLabels(); -} - void KCalcNumeralSystemView::updateLabels() { - const bool isValidNumber = m_number.has_value(); + const bool isValidNumber = m_number.type() != KNumber::TYPE_ERROR; if (isValidNumber) { - const quint64 number = m_number.value(); switch (m_base) { case 16: - m_label1->setNumber(number, 10); - m_label2->setNumber(number, 8); - m_label3->setNumber(number, 2); + m_label1->setNumber(m_number, 10); + m_label2->setNumber(m_number, 8); + m_label3->setNumber(m_number, 2); break; case 8: - m_label1->setNumber(number, 16); - m_label2->setNumber(number, 10); - m_label3->setNumber(number, 2); + m_label1->setNumber(m_number, 16); + m_label2->setNumber(m_number, 10); + m_label3->setNumber(m_number, 2); break; case 2: - m_label1->setNumber(number, 16); - m_label2->setNumber(number, 10); - m_label3->setNumber(number, 8); + m_label1->setNumber(m_number, 16); + m_label2->setNumber(m_number, 10); + m_label3->setNumber(m_number, 8); break; default: // 10 - m_label1->setNumber(number, 16); - m_label2->setNumber(number, 8); - m_label3->setNumber(number, 2); + m_label1->setNumber(m_number, 16); + m_label2->setNumber(m_number, 8); + m_label3->setNumber(m_number, 2); break; } } diff --git a/kcalc_numeralsystem_view.h b/kcalc_numeralsystem_view.h index 60b2832c5d1be3803c2cd025bac4f3e8c02ceff6..f52d6182c00aadbec9c032c3c3068229926517d6 100644 --- a/kcalc_numeralsystem_view.h +++ b/kcalc_numeralsystem_view.h @@ -8,6 +8,8 @@ #include <QWidget> +#include "knumber/knumber.h" + class QLabel; class KSqueezedTextLabel; @@ -20,7 +22,7 @@ class KCalcNumeralSystemLabel : public QWidget public: explicit KCalcNumeralSystemLabel(QWidget *parent = nullptr); - void setNumber(quint64 number, int base); + void setNumber(const KNumber &number, int base); protected: void changeEvent(QEvent *event) override; @@ -40,8 +42,7 @@ class KCalcNumeralSystemView : public QWidget public: explicit KCalcNumeralSystemView(QWidget *parent = nullptr); - void setNumber(quint64 number, int base); - void clearNumber(); + void setNumber(const KNumber &number, int base = 10); private: void updateLabels(); @@ -50,7 +51,7 @@ private: KCalcNumeralSystemLabel *m_label2 = nullptr; KCalcNumeralSystemLabel *m_label3 = nullptr; - std::optional<quint64> m_number; + KNumber m_number; int m_base = 10; }; diff --git a/knumber/knumber.cpp b/knumber/knumber.cpp index bd1b07f19465d30d210a93bcfe2c236ba235b684..94043bccc83646734b70655ce7fa13d0cb8a1619 100644 --- a/knumber/knumber.cpp +++ b/knumber/knumber.cpp @@ -16,6 +16,8 @@ #include <QStringList> #include <cmath> +using namespace Qt::StringLiterals; + QString KNumber::GroupSeparator = QStringLiteral(","); QString KNumber::DecimalSeparator = QStringLiteral("."); @@ -32,58 +34,61 @@ namespace { namespace impl { +static const QLatin1String baseChars("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + //------------------------------------------------------------------------------ // Name: increment //------------------------------------------------------------------------------ -void increment(QString &str, int position) +void increment(QString &str, int position, int base) { - for (int i = position; i >= 0; i--) { - const char last_char = str[i].toLatin1(); - switch (last_char) { - case '0': - str[i] = QLatin1Char('1'); - break; - case '1': - str[i] = QLatin1Char('2'); - break; - case '2': - str[i] = QLatin1Char('3'); - break; - case '3': - str[i] = QLatin1Char('4'); - break; - case '4': - str[i] = QLatin1Char('5'); - break; - case '5': - str[i] = QLatin1Char('6'); - break; - case '6': - str[i] = QLatin1Char('7'); - break; - case '7': - str[i] = QLatin1Char('8'); - break; - case '8': - str[i] = QLatin1Char('9'); + if (position >= str.length()) { + return; + } + + for (int i = position; i >= 0; --i) { + const int charIndex = baseChars.indexOf(str.at(i)); + if (charIndex == -1) { + if (i == position) { + return; + } + continue; // may be a decimal separator + } + + // check if we can simply increase the current value + if (charIndex < base - 1) { + str[i] = baseChars[charIndex + 1]; break; - case '9': - str[i] = QLatin1Char('0'); - if (i == 0) { - str.prepend(QLatin1Char('1')); + } + + str[i] = u'0'; + if (i == 0) { + str.prepend(u'1'); + } + } +} + +//------------------------------------------------------------------------------ +// Name: maybeIncrement +//------------------------------------------------------------------------------ +void maybeIncrement(QString &str, int position, int base) +{ + if (position < str.length() - 1) { + const int charIndex = baseChars.indexOf(str.at(position + 1)); + if (charIndex != -1) { + // check if rounding up is not needed + if (charIndex < (base + 1) / 2) { + return; } - continue; - default: - continue; } - break; } + + increment(str, position, base); } //------------------------------------------------------------------------------ // Name: round //------------------------------------------------------------------------------ -void round(QString &str, int precision, const QString &decimalSeparator) +void round(QString &str, int precision, const QString &decimalSeparator, int base) { int decimalSymbolPos = str.indexOf(decimalSeparator); if (decimalSymbolPos == -1) { @@ -103,10 +108,7 @@ void round(QString &str, int precision, const QString &decimalSeparator) } else if (extraZeroNeeded > 0) { str.append(QString().fill(QLatin1Char('0'), extraZeroNeeded)); } else { - // decide whether to round up or down based on the digit after desired length - if (str.at(desiredLength).toLatin1() >= '5') { - increment(str, desiredLength - 1); - } + maybeIncrement(str, desiredLength - 1, base); decimalSymbolPos = str.indexOf(decimalSeparator); str.truncate(decimalSymbolPos + precision + 1); } @@ -118,37 +120,33 @@ void round(QString &str, int precision, const QString &decimalSeparator) //------------------------------------------------------------------------------ // Name: round //------------------------------------------------------------------------------ -QString KNumber::round(const QString &s, int precision) const +QString KNumber::round(const QString &s, int precision, int base) const { - QString tmp = s; + if (precision < 0) { + return s; + } - const QRegularExpression rx(QString(QLatin1String(R"(^[+-]?\d+(%1\d+)*(e[+-]?\d+)?$)")).arg(QRegularExpression::escape(DecimalSeparator))); + static const QLatin1String numRegexStr(R"(^[+-]?([0-9A-F]+(?:%1[0-9A-F]+)?)([pe@][+-]?[0-9]+)?$)"); - if (precision < 0 || !rx.match(tmp).hasMatch()) { + const QRegularExpression numRegex(numRegexStr.arg(QRegularExpression::escape(DecimalSeparator))); + const QRegularExpressionMatch match = numRegex.match(s); + if (!match.hasMatch()) { return s; } - // Skip the sign (for now) - const bool neg = (tmp[0] == QLatin1Char('-')); - if (neg || tmp[0] == QLatin1Char('+')) { + QString tmp = s; + const bool neg = (tmp.at(0) == u'-'); + if (neg || tmp.at(0) == u'+') { tmp.remove(0, 1); } - // Split off exponential part (including 'e'-symbol) - QString mantString = tmp.section(QLatin1Char('e'), 0, 0, QString::SectionCaseInsensitiveSeps); - QString expString = tmp.section(QLatin1Char('e'), 1, 1, QString::SectionCaseInsensitiveSeps | QString::SectionIncludeLeadingSep); - - if (expString.length() == 1) { - expString.clear(); - } - - impl::round(mantString, precision, KNumber::decimalSeparator()); - + QString significand = match.captured(1); + impl::round(significand, precision, DecimalSeparator, base); if (neg) { - mantString.prepend(QLatin1Char('-')); + significand.prepend(u'-'); } - return mantString + expString; + return significand + match.captured(2); } //------------------------------------------------------------------------------ @@ -650,6 +648,50 @@ QString KNumber::toQString(int width, int precision) const } } +//------------------------------------------------------------------------------ +// Name: toStringInBase +//------------------------------------------------------------------------------ +QString KNumber::toStringInBase(int base, int width, int precision) const +{ + if (base < 2 || base > 36) { + return QString(); + } + + if (value_->is_zero()) { + return "0"_L1; + } + + QString str = value_->toStringInBase(base, width); + + const Type numType = type(); + if (numType != TYPE_ERROR) { + if (base == 16) { + // avoid uppercasing the exponent prefix "p" + static const QRegularExpression afRegex("([a-f]+)"_L1); + for (const auto &match : afRegex.globalMatch(str)) { + str.replace(match.capturedStart(1), match.capturedLength(1), match.captured(1).toUpper()); + } + } else if (base > 10) { + str = str.toUpper(); + } + + switch (numType) { + case TYPE_FLOAT: + case TYPE_FRACTION: + localizeDecimalSeparator(str); + break; + default: + break; + } + + if (precision >= 0 && precision < width) { + str = round(str, precision, base); + } + } + + return str; +} + //------------------------------------------------------------------------------ // Name: localizeDecimalSeparator // Desc: replaces the internal '.' as decimal separator diff --git a/knumber/knumber.h b/knumber/knumber.h index a77c3a06873546ae1489aa2c6e4a21f5ccdec536..aacdd4cafb3c81e3d49adb4d87b08ffa3ce0916a 100644 --- a/knumber/knumber.h +++ b/knumber/knumber.h @@ -96,11 +96,12 @@ public: public: QString toQString(int width = -1, int precision = -1) const; + QString toStringInBase(int base, int width = -1, int precision = -1) const; quint64 toUint64() const; qint64 toInt64() const; private: - QString round(const QString &s, int precision) const; + QString round(const QString &s, int precision, int base = 10) const; public: KNumber abs() const; diff --git a/knumber/knumber_base.h b/knumber/knumber_base.h index 2d97609ee98d4005bdef5db0509361a5814350c7..d3c10236ed95c0f67ab9a730445f7257b026859d 100644 --- a/knumber/knumber_base.h +++ b/knumber/knumber_base.h @@ -30,6 +30,7 @@ public: public: virtual QString toString(int precision) const = 0; + virtual QString toStringInBase(int base, int precision) const = 0; virtual quint64 toUint64() const = 0; virtual qint64 toInt64() const = 0; diff --git a/knumber/knumber_error.cpp b/knumber/knumber_error.cpp index 5da6f5cbebae6e8973c53a1790a133eac79d9831..8e19ef0446f6a2717e90b035b271361ce76e2d5f 100644 --- a/knumber/knumber_error.cpp +++ b/knumber/knumber_error.cpp @@ -99,6 +99,16 @@ QString knumber_error::toString(int precision) const } } +//------------------------------------------------------------------------------ +// Name: toStringInBase +//------------------------------------------------------------------------------ +QString knumber_error::toStringInBase(int base, int precision) const +{ + Q_UNUSED(base); + + return toString(precision); +} + //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ diff --git a/knumber/knumber_error.h b/knumber/knumber_error.h index a560c019e4334399885ec2124daee1d8b3e192c1..cfe7c8af38fb059728c97a56e298d9a729a19e6b 100644 --- a/knumber/knumber_error.h +++ b/knumber/knumber_error.h @@ -30,6 +30,7 @@ public: public: QString toString(int precision) const override; + QString toStringInBase(int base, int precision) const override; quint64 toUint64() const override; qint64 toInt64() const override; diff --git a/knumber/knumber_float.cpp b/knumber/knumber_float.cpp index ea2ac1b113bc7f50e5736c8bc300475e638cb9bf..83d059a4ab6a7e6899c55aac98cf996554f8f388 100644 --- a/knumber/knumber_float.cpp +++ b/knumber/knumber_float.cpp @@ -620,6 +620,59 @@ QString knumber_float::toString(int precision) const return QLatin1String(&buf[0]); } +//------------------------------------------------------------------------------ +// Name: toStringInBase +//------------------------------------------------------------------------------ +QString knumber_float::toStringInBase(int base, int precision) const +{ + if (precision < 0) { + precision = 3 * mpf_get_default_prec() / 10; + } + + const size_t bufferSize = std::max(precision + 2, 7); + QByteArray buffer(bufferSize, Qt::Uninitialized); + mpfr_exp_t exp; + mpfr_get_str(buffer.data(), &exp, base, precision, mpfr_, rounding_mode); + + const auto trimTrailingZeros = [&buffer](const int from, const int count) { + auto i = std::find_if(buffer.crend() - from - count, buffer.crend() - from, [](const char c) { + return c != '0'; + }); + buffer.erase(i.base(), buffer.cbegin() + from + count); + }; + + const int offset = buffer.at(0) == '-' ? 1 : 0; + if (exp > precision || exp < -3) { + const int pos = offset + 1; + trimTrailingZeros(pos, precision - 1); + if (buffer.at(pos) != '\0') { + buffer.insert(pos, '.'); + } + const char expPrefix = base == 2 || base == 16 ? 'p' : base > 10 ? '@' : 'e'; + if (base == 2 || base == 16) { + exp = std::log2(std::pow(base, exp - 1)); + } else { + --exp; + } + + return QString::asprintf("%s%c%+.2li", buffer.constData(), expPrefix, exp); + } + + if (exp <= 0) { + const int pos = offset; + trimTrailingZeros(pos, precision); + buffer.insert(pos, "0." + QByteArray(std::abs(exp), '0')); + } else if (exp < precision) { + const int pos = exp + offset; + trimTrailingZeros(pos, precision - exp); + if (buffer.at(pos) != '\0') { + buffer.insert(pos, '.'); + } + } + + return QLatin1String(buffer.constData()); +} + //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ diff --git a/knumber/knumber_float.h b/knumber/knumber_float.h index f33dcbbf5b5ee6fe6b5b1bdabe4c7ce56b58f1c2..e009bcf25d5c12d26d5dedcc2c1f07e0860871ac 100644 --- a/knumber/knumber_float.h +++ b/knumber/knumber_float.h @@ -43,6 +43,7 @@ private: public: QString toString(int precision) const override; + QString toStringInBase(int base, int precision) const override; quint64 toUint64() const override; qint64 toInt64() const override; diff --git a/knumber/knumber_fraction.cpp b/knumber/knumber_fraction.cpp index dde0c59e1efeece129bcfd42cd6fa02e4c39968c..eab6d98a6d10b8c6ec092648a156c7a414b8d545 100644 --- a/knumber/knumber_fraction.cpp +++ b/knumber/knumber_fraction.cpp @@ -781,6 +781,40 @@ QString knumber_fraction::toString(int precision) const } } +//------------------------------------------------------------------------------ +// Name: toStringInBase +//------------------------------------------------------------------------------ +QString knumber_fraction::toStringInBase(int base, int precision) const +{ + if (knumber_fraction::default_fractional_output) { + knumber_integer floor(this); + if (split_off_integer_for_fraction_output && !floor.is_zero()) { + knumber_integer numerator(0), denominator(0); + mpq_get_num(numerator.mpz_, mpq_); + mpq_get_den(denominator.mpz_, mpq_); + + numerator.sub(floor.mul(&denominator)); + if (numerator.sign() < 0) { + numerator.neg(); + } + + const QString floorStr = floor.toStringInBase(base, precision); + const QString numeratorStr = numerator.toStringInBase(base, precision); + const QString denominatorStr = denominator.toStringInBase(base, precision); + + return QLatin1String("%1 %2/%3").arg(floorStr, numeratorStr, denominatorStr); + } else { + const size_t bufferSize = mpz_sizeinbase(mpq_numref(mpq_), base) + mpz_sizeinbase(mpq_denref(mpq_), base) + 3; + QByteArray buffer(bufferSize, Qt::Uninitialized); + mpq_get_str(buffer.data(), base, mpq_); + + return QLatin1String(buffer.constData()); + } + } else { + return knumber_float(this).toStringInBase(base, precision); + } +} + //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ diff --git a/knumber/knumber_fraction.h b/knumber/knumber_fraction.h index 4c7ea26449ca79b46044ce4f586d5ec68ba1c8f4..651a28aeca04666b7efa8d2115a3668f91bd016f 100644 --- a/knumber/knumber_fraction.h +++ b/knumber/knumber_fraction.h @@ -41,6 +41,7 @@ public: public: QString toString(int precision) const override; + QString toStringInBase(int base, int precision) const override; quint64 toUint64() const override; qint64 toInt64() const override; diff --git a/knumber/knumber_integer.cpp b/knumber/knumber_integer.cpp index 594c9c8115b910156edf497f1c06944ce302b065..94e641bb3987a8e3903721644c49fb2bc0f189d4 100644 --- a/knumber/knumber_integer.cpp +++ b/knumber/knumber_integer.cpp @@ -709,6 +709,22 @@ QString knumber_integer::toString(int precision) const return QLatin1String(&buf[0]); } +//------------------------------------------------------------------------------ +// Name: toStringInBase +//------------------------------------------------------------------------------ +QString knumber_integer::toStringInBase(int base, int precision) const +{ + if (precision > 0) { + return knumber_float(this).toStringInBase(base, precision); + } + + const size_t bufferSize = mpz_sizeinbase(mpz_, base) + 2; + QByteArray buffer(bufferSize, Qt::Uninitialized); + mpz_get_str(buffer.data(), base, mpz_); + + return QLatin1String(buffer.constData()); +} + //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ diff --git a/knumber/knumber_integer.h b/knumber/knumber_integer.h index 97efd656f4c604abbd5649eb3c22b18485152d51..17b201e614161f6947b55a6701157680040c31b2 100644 --- a/knumber/knumber_integer.h +++ b/knumber/knumber_integer.h @@ -33,6 +33,7 @@ public: public: QString toString(int precision) const override; + QString toStringInBase(int base, int precision) const override; quint64 toUint64() const override; qint64 toInt64() const override;
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor