diff --git a/CMakeLists.txt b/CMakeLists.txt index bdd1769..f88bcb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,140 +1,148 @@ cmake_minimum_required(VERSION 3.5) # KDE Application Version, managed by release script set (KDE_APPLICATIONS_VERSION_MAJOR "19") set (KDE_APPLICATIONS_VERSION_MINOR "07") set (KDE_APPLICATIONS_VERSION_MICRO "70") set (KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}") project(kcalc VERSION ${KDE_APPLICATIONS_VERSION}) set(QT_MIN_VERSION "5.9.0") set(KF5_MIN_VERSION "5.46.0") find_package (ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set (CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) include(KDEInstallDirs) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMMarkAsTest) include(FeatureSummary) include(ECMAddAppIcon) include(ECMSetupVersion) ## Generate header with version number ecm_setup_version(${KDE_APPLICATIONS_VERSION} VARIABLE_PREFIX KCALC VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kcalc_version.h" ) find_package (Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core Widgets ) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Crash Config ConfigWidgets DocTools GuiAddons I18n Init Notifications XmlGui ) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" ${CMAKE_SOURCE_DIR}/cmake/modules) kde_enable_exceptions() find_package(GMP) set_package_properties(GMP PROPERTIES DESCRIPTION "The GNU Multiple Precision Arithmetic Library" URL "https://gmplib.org/" TYPE REQUIRED PURPOSE "Required for building KCalc." ) +find_package(MPFR) +set_package_properties(MPFR PROPERTIES + DESCRIPTION "The GNU Multiple Precision Floating-Point Reliable Library" + URL "https://www.mpfr.org/" + TYPE REQUIRED + PURPOSE "Required for building KCalc." +) + include(CheckTypeSize) include(CheckIncludeFiles) check_include_files(ieeefp.h HAVE_IEEEFP_H) check_type_size("signed long" SIZEOF_SIGNED_LONG) check_type_size("unsigned long" SIZEOF_UNSIGNED_LONG) configure_file(config-kcalc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kcalc.h ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/knumber ${GMP_INCLUDE_DIR} ) ########### next target ############### # Needs absolute paths due to the test program for knumber set(libknumber_la_SRCS ${kcalc_SOURCE_DIR}/knumber/knumber.cpp ${kcalc_SOURCE_DIR}/knumber/knumber_error.cpp ${kcalc_SOURCE_DIR}/knumber/knumber_float.cpp ${kcalc_SOURCE_DIR}/knumber/knumber_fraction.cpp ${kcalc_SOURCE_DIR}/knumber/knumber_integer.cpp ${kcalc_SOURCE_DIR}/knumber/knumber_operators.cpp ) add_subdirectory( knumber ) # add_subdirectory( tests ) set(kcalc_KDEINIT_SRCS ${libknumber_la_SRCS} kcalc.cpp bitbutton.cpp kcalc_bitset.cpp kcalc_button.cpp kcalc_const_button.cpp kcalc_const_menu.cpp kcalc_core.cpp kcalcdisplay.cpp kcalc_statusbar.cpp stats.cpp ) ki18n_wrap_ui(kcalc_KDEINIT_SRCS kcalc.ui constants.ui colors.ui fonts.ui general.ui) kconfig_add_kcfg_files(kcalc_KDEINIT_SRCS kcalc_settings.kcfgc ) # Sets the icon on Windows and OSX file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*.png") ecm_add_app_icon(kcalc_KDEINIT_SRCS ICONS ${ICONS_SRCS}) kf5_add_kdeinit_executable( kcalc ${kcalc_KDEINIT_SRCS}) target_link_libraries(kdeinit_kcalc Qt5::Core Qt5::Widgets KF5::ConfigWidgets KF5::GuiAddons KF5::I18n KF5::Notifications KF5::XmlGui KF5::Crash ${GMP_LIBRARIES} ${MPFR_LIBRARIES} ) install(TARGETS kdeinit_kcalc ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) target_link_libraries( kcalc kdeinit_kcalc ) install(TARGETS kcalc ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ) ########### install files ############### install( PROGRAMS org.kde.kcalc.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install( FILES org.kde.kcalc.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install( FILES kcalc.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR}) install( FILES kcalcui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/kcalc) install( FILES scienceconstants.xml DESTINATION ${KDE_INSTALL_DATADIR}/kcalc) install( FILES kcalcrc.upd DESTINATION ${KDE_INSTALL_DATADIR}/kconf_update) add_subdirectory(doc) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/knumber/knumber.cpp b/knumber/knumber.cpp index 617b039..1b10e02 100644 --- a/knumber/knumber.cpp +++ b/knumber/knumber.cpp @@ -1,946 +1,946 @@ /* Copyright (C) 2001 - 2013 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "knumber.h" #include "knumber_base.h" #include "knumber_error.h" #include "knumber_float.h" #include "knumber_fraction.h" #include "knumber_integer.h" #include #include #include #include QString KNumber::GroupSeparator = QStringLiteral(","); QString KNumber::DecimalSeparator = QStringLiteral("."); const KNumber KNumber::Zero(QStringLiteral("0")); const KNumber KNumber::One(QStringLiteral("1")); const KNumber KNumber::NegOne(QStringLiteral("-1")); const KNumber KNumber::PosInfinity(QStringLiteral("inf")); const KNumber KNumber::NegInfinity(QStringLiteral("-inf")); const KNumber KNumber::NaN(QStringLiteral("nan")); namespace { namespace impl { //------------------------------------------------------------------------------ // Name: increment //------------------------------------------------------------------------------ void increment(QString &str, int position) { 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'); break; case '9': str[i] = QLatin1Char('0'); if (i == 0) { str.prepend(QLatin1Char('1')); } continue; case '.': continue; } break; } } //------------------------------------------------------------------------------ // Name: round //------------------------------------------------------------------------------ void round(QString &str, int precision) { // Cut off if more digits in fractional part than 'precision' int decimalSymbolPos = str.indexOf(QLatin1Char('.')); if (decimalSymbolPos == -1) { if (precision == 0) { return; } else if (precision > 0) { // add dot if missing (and needed) str.append(QLatin1Char('.')); decimalSymbolPos = str.length() - 1; } } // fill up with more than enough zeroes (in case fractional part too short) str.append(QString().fill(QLatin1Char('0'), precision)); // Now decide whether to round up or down const char last_char = str[decimalSymbolPos + precision + 1].toLatin1(); switch (last_char) { case '0': case '1': case '2': case '3': case '4': // nothing to do, rounding down break; case '5': case '6': case '7': case '8': case '9': // rounding up increment(str, decimalSymbolPos + precision); break; default: break; } decimalSymbolPos = str.indexOf(QLatin1Char('.')); str.truncate(decimalSymbolPos + precision + 1); // if precision == 0 delete also '.' if (precision == 0) { str = str.section(QLatin1Char('.'), 0, 0); } } } //------------------------------------------------------------------------------ // Name: round //------------------------------------------------------------------------------ QString round(const QString &s, int precision) { QString tmp = s; if (precision < 0 || !QRegExp(QLatin1String("^[+-]?\\d+(\\.\\d+)*(e[+-]?\\d+)?$")).exactMatch(tmp)) { return s; } // Skip the sign (for now) const bool neg = (tmp[0] == QLatin1Char('-')); if (neg || tmp[0] == QLatin1Char('+')) { 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); if (neg) { mantString.prepend(QLatin1Char('-')); } return mantString + expString; } } //------------------------------------------------------------------------------ // Name: setGroupSeparator //------------------------------------------------------------------------------ void KNumber::setGroupSeparator(const QString &ch) { GroupSeparator = ch; } //------------------------------------------------------------------------------ // Name: setDecimalSeparator //------------------------------------------------------------------------------ void KNumber::setDecimalSeparator(const QString &ch) { DecimalSeparator = ch; } //------------------------------------------------------------------------------ // Name: groupSeparator //------------------------------------------------------------------------------ QString KNumber::groupSeparator() { return GroupSeparator; } //------------------------------------------------------------------------------ // Name: decimalSeparator //------------------------------------------------------------------------------ QString KNumber::decimalSeparator() { return DecimalSeparator; } //------------------------------------------------------------------------------ // Name: setDefaultFloatPrecision //------------------------------------------------------------------------------ void KNumber::setDefaultFloatPrecision(int precision) { // Need to transform decimal digits into binary digits - const unsigned long int bin_prec = static_cast(double(precision) * M_LN10 / M_LN2 + 1); - mpf_set_default_prec(bin_prec); + const unsigned long int bin_prec = static_cast(::ceil(precision * M_LN10 / M_LN2) + 1); + mpfr_set_default_prec(static_cast(bin_prec)); } //------------------------------------------------------------------------------ // Name: setSplitoffIntegerForFractionOutput //------------------------------------------------------------------------------ void KNumber::setSplitoffIntegerForFractionOutput(bool x) { detail::knumber_fraction::set_split_off_integer_for_fraction_output(x); } //------------------------------------------------------------------------------ // Name: setDefaultFractionalInput //------------------------------------------------------------------------------ void KNumber::setDefaultFractionalInput(bool x) { detail::knumber_fraction::set_default_fractional_input(x); } //------------------------------------------------------------------------------ // Name: setDefaultFloatOutput //------------------------------------------------------------------------------ void KNumber::setDefaultFloatOutput(bool x) { detail::knumber_fraction::set_default_fractional_output(!x); } //------------------------------------------------------------------------------ // Name: Pi //------------------------------------------------------------------------------ KNumber KNumber::Pi() { // TODO: after 4.10 release: // create a new constructor which works just like the normal QString // accepting constructor, but allows us to specify separator // characters, this will allow things to be done slightly more // efficiently QString s(QStringLiteral("3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068")); s.replace(QLatin1Char('.'), DecimalSeparator); return KNumber(s); } //------------------------------------------------------------------------------ // Name: Euler //------------------------------------------------------------------------------ KNumber KNumber::Euler() { // TODO: after 4.10 release: // create a new constructor which works just like the normal QString // accepting constructor, but allows us to specify separator // characters, this will allow things to be done slightly more // efficiently QString s(QStringLiteral("2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274")); s.replace(QLatin1Char('.'), DecimalSeparator); return KNumber(s); } //------------------------------------------------------------------------------ // Name: KNumber //------------------------------------------------------------------------------ KNumber::KNumber() : value_(new detail::knumber_integer(0)) { } //------------------------------------------------------------------------------ // Name: KNumber //------------------------------------------------------------------------------ KNumber::KNumber(const QString &s) : value_(nullptr) { const QRegExp special_regex(QLatin1String("^(inf|-inf|nan)$")); const QRegExp integer_regex(QLatin1String("^[+-]?\\d+$")); const QRegExp fraction_regex(QLatin1String("^[+-]?\\d+/\\d+$")); const QRegExp float_regex(QString(QLatin1String("^([+-]?\\d*)(%1\\d*)?(e([+-]?\\d+))?$")).arg(QRegExp::escape(DecimalSeparator))); if (special_regex.exactMatch(s)) { value_ = new detail::knumber_error(s); } else if (integer_regex.exactMatch(s)) { value_ = new detail::knumber_integer(s); } else if (fraction_regex.exactMatch(s)) { value_ = new detail::knumber_fraction(s); simplify(); } else if (float_regex.exactMatch(s)) { if(detail::knumber_fraction::default_fractional_input) { const QStringList list = float_regex.capturedTexts(); if(list.size() == 5) { const QString ipart = list[1]; const QString fpart = list[2]; const QString epart = list[3]; const int e_val = list[4].toInt(); QString num = ipart + fpart.mid(1); QString den = QLatin1String("1") + QString(fpart.size() - 1, QLatin1Char('0')); if(e_val < 0) { den = den + QString(::abs(e_val), QLatin1Char('0')); } else if(e_val > 0) { num = num + QString(::abs(e_val), QLatin1Char('0')); } value_ = new detail::knumber_fraction(QStringLiteral("%1/%2").arg(num, den)); simplify(); return; } } // we need to normalize the decimal separator to US style because that's // the only type that the GMP function accept QString new_s = s; new_s.replace(DecimalSeparator, QLatin1String(".")); value_ = new detail::knumber_float(new_s); simplify(); } else { value_ = new detail::knumber_error(detail::knumber_error::ERROR_UNDEFINED); } } //------------------------------------------------------------------------------ // Name: KNumber //------------------------------------------------------------------------------ KNumber::KNumber(qint32 value) : value_(new detail::knumber_integer(value)) { } //------------------------------------------------------------------------------ // Name: KNumber //------------------------------------------------------------------------------ KNumber::KNumber(qint64 value) : value_(new detail::knumber_integer(value)) { } //------------------------------------------------------------------------------ // Name: KNumber //------------------------------------------------------------------------------ KNumber::KNumber(quint32 value) : value_(new detail::knumber_integer(value)) { } //------------------------------------------------------------------------------ // Name: KNumber //------------------------------------------------------------------------------ KNumber::KNumber(quint64 value) : value_(new detail::knumber_integer(value)) { } //------------------------------------------------------------------------------ // Name: KNumber //------------------------------------------------------------------------------ KNumber::KNumber(qint64 num, quint64 den) : value_(new detail::knumber_fraction(num, den)) { } //------------------------------------------------------------------------------ // Name: KNumber //------------------------------------------------------------------------------ KNumber::KNumber(quint64 num, quint64 den) : value_(new detail::knumber_fraction(num, den)) { } #ifdef HAVE_LONG_DOUBLE //------------------------------------------------------------------------------ // Name: KNumber //------------------------------------------------------------------------------ KNumber::KNumber(long double value) : value_(new detail::knumber_float(value)) { simplify(); } #endif //------------------------------------------------------------------------------ // Name: KNumber //------------------------------------------------------------------------------ KNumber::KNumber(double value) : value_(new detail::knumber_float(value)) { simplify(); } //------------------------------------------------------------------------------ // Name: KNumber //------------------------------------------------------------------------------ KNumber::KNumber(const KNumber &other) : value_(nullptr) { if(&other != this) { value_ = other.value_->clone(); } } //------------------------------------------------------------------------------ // Name: ~KNumber //------------------------------------------------------------------------------ KNumber::~KNumber() { delete value_; } //------------------------------------------------------------------------------ // Name: type //------------------------------------------------------------------------------ KNumber::Type KNumber::type() const { if(dynamic_cast(value_)) { return TYPE_INTEGER; } else if(dynamic_cast(value_)) { return TYPE_FLOAT; } else if(dynamic_cast(value_)) { return TYPE_FRACTION; } else if(dynamic_cast(value_)) { return TYPE_ERROR; } else { Q_ASSERT(0); return TYPE_ERROR; } } //------------------------------------------------------------------------------ // Name: operator= //------------------------------------------------------------------------------ KNumber &KNumber::operator=(const KNumber &rhs) { KNumber(rhs).swap(*this); return *this; } //------------------------------------------------------------------------------ // Name: swap //------------------------------------------------------------------------------ void KNumber::swap(KNumber &other) { qSwap(value_, other.value_); } //------------------------------------------------------------------------------ // Name: integerPart //------------------------------------------------------------------------------ KNumber KNumber::integerPart() const { KNumber x(*this); if(detail::knumber_integer *const p = dynamic_cast(value_)) { // NO-OP Q_UNUSED(p); } else if(detail::knumber_float *const p = dynamic_cast(value_)) { detail::knumber_base *v = new detail::knumber_integer(p); qSwap(v, x.value_); delete v; } else if(detail::knumber_fraction *const p = dynamic_cast(value_)) { detail::knumber_base *v = new detail::knumber_integer(p); qSwap(v, x.value_); delete v; } else if(detail::knumber_error *const p = dynamic_cast(value_)) { // NO-OP Q_UNUSED(p); } else { Q_ASSERT(0); } return x; } //------------------------------------------------------------------------------ // Name: simplify //------------------------------------------------------------------------------ void KNumber::simplify() { if(value_->is_integer()) { if(detail::knumber_integer *const p = dynamic_cast(value_)) { // NO-OP Q_UNUSED(p); } else if(detail::knumber_float *const p = dynamic_cast(value_)) { detail::knumber_base *v = new detail::knumber_integer(p); qSwap(v, value_); delete v; } else if(detail::knumber_fraction *const p = dynamic_cast(value_)) { detail::knumber_base *v = new detail::knumber_integer(p); qSwap(v, value_); delete v; } else if(detail::knumber_error *const p = dynamic_cast(value_)) { // NO-OP Q_UNUSED(p); } else { Q_ASSERT(0); } } } //------------------------------------------------------------------------------ // Name: operator+= //------------------------------------------------------------------------------ KNumber &KNumber::operator+=(const KNumber &rhs) { value_ = value_->add(rhs.value_); simplify(); return *this; } //------------------------------------------------------------------------------ // Name: operator-= //------------------------------------------------------------------------------ KNumber &KNumber::operator-=(const KNumber &rhs) { value_ = value_->sub(rhs.value_); simplify(); return *this; } //------------------------------------------------------------------------------ // Name: operator*= //------------------------------------------------------------------------------ KNumber &KNumber::operator*=(const KNumber &rhs) { value_ = value_->mul(rhs.value_); simplify(); return *this; } //------------------------------------------------------------------------------ // Name: operator/= //------------------------------------------------------------------------------ KNumber &KNumber::operator/=(const KNumber &rhs) { // Fix for bug #330577, x /0 is undefined, not infinity // Also indirectly fixes bug #329897, tan(90) is undefined, not infinity if(rhs == Zero) { *this = NaN; return *this; } value_ = value_->div(rhs.value_); simplify(); return *this; } //------------------------------------------------------------------------------ // Name: operator%= //------------------------------------------------------------------------------ KNumber &KNumber::operator%=(const KNumber &rhs) { value_ = value_->mod(rhs.value_); simplify(); return *this; } //------------------------------------------------------------------------------ // Name: operator&= //------------------------------------------------------------------------------ KNumber &KNumber::operator&=(const KNumber &rhs) { value_ = value_->bitwise_and(rhs.value_); return *this; } //------------------------------------------------------------------------------ // Name: operator|= //------------------------------------------------------------------------------ KNumber &KNumber::operator|=(const KNumber &rhs) { value_ = value_->bitwise_or(rhs.value_); return *this; } //------------------------------------------------------------------------------ // Name: operator^= //------------------------------------------------------------------------------ KNumber &KNumber::operator^=(const KNumber &rhs) { value_ = value_->bitwise_xor(rhs.value_); return *this; } //------------------------------------------------------------------------------ // Name: operator<< //------------------------------------------------------------------------------ KNumber &KNumber::operator<<=(const KNumber &rhs) { value_ = value_->bitwise_shift(rhs.value_); return *this; } //------------------------------------------------------------------------------ // Name: operator>>= //------------------------------------------------------------------------------ KNumber &KNumber::operator>>=(const KNumber &rhs) { const KNumber rhs_neg(-rhs); value_ = value_->bitwise_shift(rhs_neg.value_); return *this; } //------------------------------------------------------------------------------ // Name: operator- //------------------------------------------------------------------------------ KNumber KNumber::operator-() const { KNumber x(*this); x.value_ = x.value_->neg(); return x; } //------------------------------------------------------------------------------ // Name: operator~ //------------------------------------------------------------------------------ KNumber KNumber::operator~() const { KNumber x(*this); x.value_ = x.value_->cmp(); return x; } //------------------------------------------------------------------------------ // Name: toQString //------------------------------------------------------------------------------ QString KNumber::toQString(int width, int precision) const { if(value_->is_zero()) { return QStringLiteral("0"); } QString s; if(detail::knumber_integer *const p = dynamic_cast(value_)) { if(width > 0) { s = detail::knumber_float(p).toString(width); } else { s = value_->toString(width); } } else if(detail::knumber_float *const p = dynamic_cast(value_)) { if(width > 0) { s = value_->toString(width); } else { s = value_->toString(3 * mpf_get_default_prec() / 10); } } else if(detail::knumber_fraction *const p = dynamic_cast(value_)) { s = value_->toString(width); } else { return value_->toString(width); } // now do some rounding to make sure things are displayed reasonably if (precision >= 0) { return round(s, precision); } else { return s; } } //------------------------------------------------------------------------------ // Name: toUint64 //------------------------------------------------------------------------------ quint64 KNumber::toUint64() const { return value_->toUint64(); } //------------------------------------------------------------------------------ // Name: toInt64 //------------------------------------------------------------------------------ qint64 KNumber::toInt64() const { return value_->toInt64(); } //------------------------------------------------------------------------------ // Name: abs //------------------------------------------------------------------------------ KNumber KNumber::abs() const { KNumber z(*this); z.value_ = z.value_->abs(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: cbrt //------------------------------------------------------------------------------ KNumber KNumber::cbrt() const { KNumber z(*this); z.value_ = z.value_->cbrt(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: sqrt //------------------------------------------------------------------------------ KNumber KNumber::sqrt() const { KNumber z(*this); z.value_ = z.value_->sqrt(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: pow //------------------------------------------------------------------------------ KNumber KNumber::pow(const KNumber &x) const { // Fix for bug #330711 (pow(0, -x) was causing crashes // Fix for bug #330597 (pow(0,0) was 1 now it is NaN // Thanks to Raushan Kumar for identifying the issue and submitting // patches if(*this == Zero && x <= Zero) { return NaN; } // if the LHS is a special then we can use this function // no matter what, cause the result is a special too if(!dynamic_cast(value_)) { // number much bigger than this tend to crash GMP with // an abort if(x > KNumber(QStringLiteral("1000000000"))) { return PosInfinity; } } KNumber z(*this); z.value_ = z.value_->pow(x.value_); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: sin //------------------------------------------------------------------------------ KNumber KNumber::sin() const { KNumber z(*this); z.value_ = z.value_->sin(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: cos //------------------------------------------------------------------------------ KNumber KNumber::cos() const { KNumber z(*this); z.value_ = z.value_->cos(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: tan //------------------------------------------------------------------------------ KNumber KNumber::tan() const { KNumber z(*this); z.value_ = z.value_->tan(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: tgamma //------------------------------------------------------------------------------ KNumber KNumber::tgamma() const { KNumber z(*this); if(z > KNumber(QStringLiteral("10000000000"))) { return PosInfinity; } z.value_ = z.value_->tgamma(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: asin //------------------------------------------------------------------------------ KNumber KNumber::asin() const { KNumber z(*this); z.value_ = z.value_->asin(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: acos //------------------------------------------------------------------------------ KNumber KNumber::acos() const { KNumber z(*this); z.value_ = z.value_->acos(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: atan //------------------------------------------------------------------------------ KNumber KNumber::atan() const { KNumber z(*this); z.value_ = z.value_->atan(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: sinh //------------------------------------------------------------------------------ KNumber KNumber::sinh() const { KNumber z(*this); z.value_ = z.value_->sinh(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: cosh //------------------------------------------------------------------------------ KNumber KNumber::cosh() const { KNumber z(*this); z.value_ = z.value_->cosh(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: tanh //------------------------------------------------------------------------------ KNumber KNumber::tanh() const { KNumber z(*this); z.value_ = z.value_->tanh(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: asinh //------------------------------------------------------------------------------ KNumber KNumber::asinh() const { KNumber z(*this); z.value_ = z.value_->asinh(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: acosh //------------------------------------------------------------------------------ KNumber KNumber::acosh() const { KNumber z(*this); z.value_ = z.value_->acosh(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: atanh //------------------------------------------------------------------------------ KNumber KNumber::atanh() const { KNumber z(*this); z.value_ = z.value_->atanh(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: factorial //------------------------------------------------------------------------------ KNumber KNumber::factorial() const { KNumber z(*this); // number much bigger than this tend to crash GMP with // an abort if(z > KNumber(QStringLiteral("10000000000"))) { return PosInfinity; } z.value_ = z.value_->factorial(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: log2 //------------------------------------------------------------------------------ KNumber KNumber::log2() const { KNumber z(*this); z.value_ = z.value_->log2(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: log10 //------------------------------------------------------------------------------ KNumber KNumber::log10() const { KNumber z(*this); z.value_ = z.value_->log10(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: ln //------------------------------------------------------------------------------ KNumber KNumber::ln() const { KNumber z(*this); z.value_ = z.value_->ln(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: floor //------------------------------------------------------------------------------ KNumber KNumber::floor() const { KNumber z(*this); z.value_ = z.value_->floor(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: ceil //------------------------------------------------------------------------------ KNumber KNumber::ceil() const { KNumber z(*this); z.value_ = z.value_->ceil(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: exp2 //------------------------------------------------------------------------------ KNumber KNumber::exp2() const { KNumber z(*this); z.value_ = z.value_->exp2(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: exp10 //------------------------------------------------------------------------------ KNumber KNumber::exp10() const { KNumber z(*this); z.value_ = z.value_->exp10(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: exp //------------------------------------------------------------------------------ KNumber KNumber::exp() const { KNumber z(*this); z.value_ = z.value_->exp(); z.simplify(); return z; } //------------------------------------------------------------------------------ // Name: bin //------------------------------------------------------------------------------ KNumber KNumber::bin(const KNumber &x) const { KNumber z(*this); z.value_ = z.value_->bin(x.value_); z.simplify(); return z; } diff --git a/knumber/knumber_base.h b/knumber/knumber_base.h index 150d86f..4da32c5 100644 --- a/knumber/knumber_base.h +++ b/knumber/knumber_base.h @@ -1,118 +1,115 @@ /* Copyright (C) 2001 - 2013 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KNUMBER_BASE_H_ #define KNUMBER_BASE_H_ // Workaround: include before gmp.h to fix build with gcc-4.9 #include #include - -#ifdef KNUMBER_USE_MPFR #include -#endif #include #include namespace detail { class knumber_error; class knumber_integer; class knumber_fraction; class knumber_float; class knumber_base { public: virtual ~knumber_base() = default; public: virtual knumber_base *clone() = 0; public: virtual QString toString(int precision) const = 0; virtual quint64 toUint64() const = 0; virtual qint64 toInt64() const = 0; public: virtual bool is_integer() const = 0; virtual bool is_zero() const = 0; virtual int sign() const = 0; public: // basic math virtual knumber_base *add(knumber_base *rhs) = 0; virtual knumber_base *sub(knumber_base *rhs) = 0; virtual knumber_base *mul(knumber_base *rhs) = 0; virtual knumber_base *div(knumber_base *rhs) = 0; virtual knumber_base *mod(knumber_base *rhs) = 0; public: // logical operators virtual knumber_base *bitwise_and(knumber_base *rhs) = 0; virtual knumber_base *bitwise_xor(knumber_base *rhs) = 0; virtual knumber_base *bitwise_or(knumber_base *rhs) = 0; virtual knumber_base *bitwise_shift(knumber_base *rhs) = 0; public: // algebraic functions virtual knumber_base *pow(knumber_base *rhs) = 0; virtual knumber_base *neg() = 0; virtual knumber_base *cmp() = 0; virtual knumber_base *abs() = 0; virtual knumber_base *sqrt() = 0; virtual knumber_base *cbrt() = 0; virtual knumber_base *factorial() = 0; virtual knumber_base *reciprocal() = 0; public: // special functions virtual knumber_base *log2() = 0; virtual knumber_base *log10() = 0; virtual knumber_base *ln() = 0; virtual knumber_base *exp2() = 0; virtual knumber_base *exp10() = 0; virtual knumber_base *floor() = 0; virtual knumber_base *ceil() = 0; virtual knumber_base *exp() = 0; virtual knumber_base *bin(knumber_base *rhs) = 0; public: // trig functions virtual knumber_base *sin() = 0; virtual knumber_base *cos() = 0; virtual knumber_base *tan() = 0; virtual knumber_base *asin() = 0; virtual knumber_base *acos() = 0; virtual knumber_base *atan() = 0; virtual knumber_base *sinh() = 0; virtual knumber_base *cosh() = 0; virtual knumber_base *tanh() = 0; virtual knumber_base *asinh() = 0; virtual knumber_base *acosh() = 0; virtual knumber_base *atanh() = 0; virtual knumber_base *tgamma() = 0; public: // comparison virtual int compare(knumber_base *rhs) = 0; }; } #endif diff --git a/knumber/knumber_float.cpp b/knumber/knumber_float.cpp index 072812e..eb6317a 100644 --- a/knumber/knumber_float.cpp +++ b/knumber/knumber_float.cpp @@ -1,1028 +1,693 @@ /* Copyright (C) 2001 - 2013 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include #include "knumber_integer.h" #include "knumber_float.h" #include "knumber_fraction.h" #include "knumber_error.h" #include #include #include #ifdef _MSC_VER double log2(double x) { return log(x) / log(2); } double exp2(double x) { return exp(x * log(2)); } double exp10(double x) { return exp(x * log(10)); } #endif // NOTE: these assume IEEE floats.. #ifndef isinf #define isinf(x) ((x) != 0.0 && (x) + (x) == (x)) #endif #ifndef isnan #define isnan(x) ((x) != (x)) #endif namespace detail { -#ifdef KNUMBER_USE_MPFR const mpfr_rnd_t knumber_float::rounding_mode = MPFR_RNDN; const mpfr_prec_t knumber_float::precision = 1024; -#endif -template -knumber_base *knumber_float::execute_libc_func(double x) { - const double r = F(x); - if(isnan(r)) { +knumber_base *knumber_float::ensureIsValid(mpfr_ptr mpfr) { + if (mpfr_nan_p(mpfr)) { knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); delete this; return e; - } else if(isinf(r)) { + } else if (mpfr_inf_p(mpfr)) { knumber_error *e = new knumber_error(knumber_error::ERROR_POS_INFINITY); delete this; return e; } else { - mpf_set_d(mpf_, r); return this; } } -template -knumber_base *knumber_float::execute_libc_func(double x, double y) { - const double r = F(x, y); - if(isnan(r)) { - knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); - delete this; - return e; - } else if(isinf(r)) { - knumber_error *e = new knumber_error(knumber_error::ERROR_POS_INFINITY); - delete this; - return e; - } else { - mpf_set_d(mpf_, r); - return this; - } +template +knumber_base *knumber_float::execute_mpfr_func() { + F(mpfr_, mpfr_); + return ensureIsValid(mpfr_); +} + +template +knumber_base *knumber_float::execute_mpfr_func() { + F(mpfr_, mpfr_, rounding_mode); + return ensureIsValid(mpfr_); +} + +template +knumber_base *knumber_float::execute_mpfr_func(mpfr_srcptr op) { + F(mpfr_, mpfr_, op, rounding_mode); + return ensureIsValid(mpfr_); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_float::knumber_float(const QString &s) { - - mpf_init(mpf_); - mpf_set_str(mpf_, s.toLatin1().constData(), 10); + mpfr_set_str(new_mpfr(), s.toLatin1().constData(), 10, rounding_mode); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_float::knumber_float(double value) { Q_ASSERT(!isinf(value)); Q_ASSERT(!isnan(value)); - mpf_init_set_d(mpf_, value); + mpfr_set_d(new_mpfr(), value, rounding_mode); } #ifdef HAVE_LONG_DOUBLE //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_float::knumber_float(long double value) { Q_ASSERT(!isinf(value)); Q_ASSERT(!isnan(value)); - mpf_init_set_d(mpf_, value); + mpfr_set_ld(new_mpfr(), value, rounding_mode); } #endif //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ -knumber_float::knumber_float(mpf_t mpf) { +knumber_float::knumber_float(mpfr_t mpfr) { - mpf_init(mpf_); - mpf_set(mpf_, mpf); + mpfr_set(new_mpfr(), mpfr, rounding_mode); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_float::knumber_float(const knumber_float *value) { - - mpf_init_set(mpf_, value->mpf_); + mpfr_set(new_mpfr(), value->mpfr_, rounding_mode); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_float::knumber_float(const knumber_integer *value) { - - mpf_init(mpf_); - mpf_set_z(mpf_, value->mpz_); + mpfr_set_z(new_mpfr(), value->mpz_, rounding_mode); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_float::knumber_float(const knumber_fraction *value) { - - mpf_init(mpf_); - mpf_set_q(mpf_, value->mpq_); + mpfr_set_q(new_mpfr(), value->mpq_, rounding_mode); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::clone() { return new knumber_float(this); } +mpfr_ptr knumber_float::new_mpfr() { + mpfr_init(mpfr_); + return mpfr_; +} + //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_float::~knumber_float() { - - mpf_clear(mpf_); + mpfr_clear(mpfr_); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::add(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { knumber_float f(p); return add(&f); } else if(knumber_float *const p = dynamic_cast(rhs)) { - mpf_add(mpf_, mpf_, p->mpf_); + mpfr_add(mpfr_, mpfr_, p->mpfr_, rounding_mode); return this; } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_float f(p); return add(&f); } else if(knumber_error *const p = dynamic_cast(rhs)) { knumber_error *e = new knumber_error(p); delete this; return e; } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::sub(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { knumber_float f(p); return sub(&f); } else if(knumber_float *const p = dynamic_cast(rhs)) { - mpf_sub(mpf_, mpf_, p->mpf_); + mpfr_sub(mpfr_, mpfr_, p->mpfr_, rounding_mode); return this; } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_float f(p); return sub(&f); } else if(knumber_error *const p = dynamic_cast(rhs)) { knumber_error *e = new knumber_error(p); delete this; return e->neg(); } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::mul(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { knumber_float f(p); return mul(&f); } else if(knumber_float *const p = dynamic_cast(rhs)) { - mpf_mul(mpf_, mpf_, p->mpf_); + mpfr_mul(mpfr_, mpfr_, p->mpfr_, rounding_mode); return this; } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_float f(p); return mul(&f); } else if(knumber_error *const p = dynamic_cast(rhs)) { if(is_zero()) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } if(sign() < 0) { delete this; knumber_error *e = new knumber_error(p); return e->neg(); } else { delete this; return new knumber_error(p); } } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::div(knumber_base *rhs) { if(rhs->is_zero()) { if(sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_NEG_INFINITY); } else { delete this; return new knumber_error(knumber_error::ERROR_POS_INFINITY); } } if(knumber_integer *const p = dynamic_cast(rhs)) { knumber_float f(p); return div(&f); } else if(knumber_float *const p = dynamic_cast(rhs)) { - mpf_div(mpf_, mpf_, p->mpf_); + mpfr_div(mpfr_, mpfr_, p->mpfr_, rounding_mode); return this; } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_float f(p); return div(&f); } else if(knumber_error *const p = dynamic_cast(rhs)) { if(p->sign() > 0 || p->sign() < 0) { delete this; return new knumber_integer(0); } delete this; return new knumber_error(p); } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::mod(knumber_base *rhs) { Q_UNUSED(rhs); if(rhs->is_zero()) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } delete this; return new knumber_integer(0); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::bitwise_and(knumber_base *rhs) { Q_UNUSED(rhs); delete this; return new knumber_integer(0); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::bitwise_xor(knumber_base *rhs) { Q_UNUSED(rhs); delete this; return new knumber_integer(0); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::bitwise_or(knumber_base *rhs) { Q_UNUSED(rhs); delete this; return new knumber_integer(0); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::bitwise_shift(knumber_base *rhs) { Q_UNUSED(rhs); delete this; // NOTE: we don't support bitwise operations with non-integer operands return new knumber_error(knumber_error::ERROR_UNDEFINED); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::neg() { - - mpf_neg(mpf_, mpf_); + mpfr_neg(mpfr_, mpfr_, rounding_mode); return this; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::cmp() { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::abs() { - - mpf_abs(mpf_, mpf_); + mpfr_abs(mpfr_, mpfr_, rounding_mode); return this; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::sqrt() { if(sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_sqrt(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); -#else - mpf_sqrt(mpf_, mpf_); -#endif + mpfr_sqrt(mpfr_, mpfr_, rounding_mode); return this; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::cbrt() { - -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_cbrt(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { -#ifdef Q_CC_MSVC - return execute_libc_func< ::pow>(x, 1.0 / 3.0); -#else - return execute_libc_func< ::cbrt>(x); -#endif - } -#endif - return this; + return execute_mpfr_func< ::mpfr_cbrt>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::factorial() { if(sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } knumber_integer *i = new knumber_integer(this); delete this; return i->factorial(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::sin() { - -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_sin(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::sin>(x); - } -#endif - + return execute_mpfr_func< ::mpfr_sin>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::floor() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_floor(mpfr, mpfr); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::floor>(x); - } -#endif + return execute_mpfr_func< ::mpfr_floor>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::ceil() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_ceil(mpfr, mpfr); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::ceil>(x); - } -#endif + return execute_mpfr_func< ::mpfr_ceil>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::cos() { - -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_cos(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::cos>(x); - } -#endif + return execute_mpfr_func< ::mpfr_cos>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::tan() { - -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_tan(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::tan>(x); - } -#endif + return execute_mpfr_func< ::mpfr_tan>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::asin() { - if(mpf_cmp_d(mpf_, 1.0) > 0 || mpf_cmp_d(mpf_, -1.0) < 0) { + if (mpfr_cmp_d(mpfr_, 1.0) > 0 || mpfr_cmp_d(mpfr_, -1.0) < 0) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_asin(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::asin>(x); - } -#endif + return execute_mpfr_func< ::mpfr_asin>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------s knumber_base *knumber_float::acos() { - if(mpf_cmp_d(mpf_, 1.0) > 0 || mpf_cmp_d(mpf_, -1.0) < 0) { + if (mpfr_cmp_d(mpfr_, 1.0) > 0 || mpfr_cmp_d(mpfr_, -1.0) < 0) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_acos(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::acos>(x); - } -#endif + return execute_mpfr_func< ::mpfr_acos>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::atan() { - -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_atan(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::atan>(x); - } -#endif + return execute_mpfr_func< ::mpfr_atan>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::sinh() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_sinh(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - return execute_libc_func< ::sinh>(x); -#endif - + return execute_mpfr_func< ::mpfr_sinh>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::cosh() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_cosh(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - return execute_libc_func< ::cosh>(x); -#endif + return execute_mpfr_func< ::mpfr_cosh>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::tanh() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_tanh(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - return execute_libc_func< ::tanh>(x); -#endif + return execute_mpfr_func< ::mpfr_tanh>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::tgamma() { - -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_gamma(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - - } else { - return execute_libc_func< ::tgamma>(x); - } -#endif - + return execute_mpfr_func< ::mpfr_gamma>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::asinh() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_asinh(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - return execute_libc_func< ::asinh>(x); -#endif + return execute_mpfr_func< ::mpfr_asinh>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::acosh() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_acosh(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - return execute_libc_func< ::acosh>(x); -#endif + return execute_mpfr_func< ::mpfr_acosh>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::atanh() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_atanh(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - return execute_libc_func< ::atanh>(x); -#endif + return execute_mpfr_func< ::mpfr_atanh>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::pow(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { - mpf_pow_ui(mpf_, mpf_, mpz_get_ui(p->mpz_)); + mpfr_pow_ui(mpfr_, mpfr_, mpz_get_ui(p->mpz_), rounding_mode); if(p->sign() < 0) { return reciprocal(); } else { return this; } } else if(knumber_float *const p = dynamic_cast(rhs)) { - return execute_libc_func< ::pow>(mpf_get_d(mpf_), mpf_get_d(p->mpf_)); + return execute_mpfr_func< ::mpfr_pow>(p->mpfr_); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_float f(p); - return execute_libc_func< ::pow>(mpf_get_d(mpf_), mpf_get_d(f.mpf_)); + return execute_mpfr_func< ::mpfr_pow>(f.mpfr_); } else if(knumber_error *const p = dynamic_cast(rhs)) { if(p->sign() > 0) { knumber_error *e = new knumber_error(knumber_error::ERROR_POS_INFINITY); delete this; return e; } else if(p->sign() < 0) { knumber_integer *n = new knumber_integer(0); delete this; return n; } else { knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); delete this; return e; } } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ int knumber_float::compare(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { knumber_float f(p); return compare(&f); } else if(knumber_float *const p = dynamic_cast(rhs)) { - return mpf_cmp(mpf_, p->mpf_); + return mpfr_cmp(mpfr_, p->mpfr_); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_float f(p); return compare(&f); } else if(knumber_error *const p = dynamic_cast(rhs)) { // NOTE: any number compared to NaN/Inf/-Inf always compares less // at the moment return -1; } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ QString knumber_float::toString(int precision) const { size_t size; + if (precision > 0) { - size = gmp_snprintf(nullptr, 0, "%.*Fg", precision, mpf_) + 1; + size = static_cast(mpfr_snprintf(nullptr, 0, "%.*Rg", precision, mpfr_) + 1); } else { - size = gmp_snprintf(nullptr, 0, "%.Fg", mpf_) + 1; + size = static_cast(mpfr_snprintf(nullptr, 0, "%.Rg", mpfr_) + 1); } QScopedArrayPointer buf(new char[size]); if (precision > 0) { - gmp_snprintf(&buf[0], size, "%.*Fg", precision, mpf_); + mpfr_snprintf(&buf[0], size, "%.*Rg", precision, mpfr_); } else { - gmp_snprintf(&buf[0], size, "%.Fg", mpf_); + mpfr_snprintf(&buf[0], size, "%.Rg", mpfr_); } return QLatin1String(&buf[0]); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool knumber_float::is_integer() const { - - return mpf_integer_p(mpf_) != 0; + return mpfr_integer_p(mpfr_) != 0; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ bool knumber_float::is_zero() const { - - return mpf_sgn(mpf_) == 0; + return mpfr_zero_p(mpfr_); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ int knumber_float::sign() const { - - return mpf_sgn(mpf_); + return mpfr_sgn(mpfr_); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::reciprocal() { - - mpf_t mpf; - mpf_init_set_d(mpf, 1.0); - mpf_div(mpf_, mpf, mpf_); + mpfr_t mpfr; + mpfr_init_set_d(mpfr, 1.0, rounding_mode); + mpfr_div(mpfr_, mpfr, mpfr_, rounding_mode); + mpfr_clear(mpfr); return this; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::log2() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_log2(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::log2>(x); - } -#endif + return execute_mpfr_func< ::mpfr_log2>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::log10() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_log10(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::log10>(x); - } -#endif + return execute_mpfr_func< ::mpfr_log10>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::ln() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_log(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::log>(x); - } -#endif + return execute_mpfr_func< ::mpfr_log>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::exp2() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_exp2(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::exp2>(x); - } -#endif + return execute_mpfr_func< ::mpfr_exp2>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::exp10() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_exp10(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::pow>(10, x); - } -#endif + return execute_mpfr_func< ::mpfr_exp10>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::exp() { -#ifdef KNUMBER_USE_MPFR - mpfr_t mpfr; - mpfr_init_set_f(mpfr, mpf_, rounding_mode); - mpfr_exp(mpfr, mpfr, rounding_mode); - mpfr_get_f(mpf_, mpfr, rounding_mode); - mpfr_clear(mpfr); - return this; -#else - const double x = mpf_get_d(mpf_); - if(isinf(x)) { - delete this; - return new knumber_error(knumber_error::ERROR_POS_INFINITY); - } else { - return execute_libc_func< ::exp>(x); - } -#endif + return execute_mpfr_func< ::mpfr_exp>(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ quint64 knumber_float::toUint64() const { return knumber_integer(this).toUint64(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ qint64 knumber_float::toInt64() const { return knumber_integer(this).toInt64(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_float::bin(knumber_base *rhs) { Q_UNUSED(rhs); delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } } diff --git a/knumber/knumber_float.h b/knumber/knumber_float.h index f9ed9fc..afa4be1 100644 --- a/knumber/knumber_float.h +++ b/knumber/knumber_float.h @@ -1,135 +1,141 @@ /* Copyright (C) 2001 - 2013 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KNUMBER_FLOAT_H_ #define KNUMBER_FLOAT_H_ +#include #include "knumber_base.h" class KNumber; namespace detail { class knumber_float : public knumber_base { friend class ::KNumber; friend class knumber_error; friend class knumber_integer; friend class knumber_fraction; private: -#ifdef KNUMBER_USE_MPFR static const mpfr_rnd_t rounding_mode; static const mpfr_prec_t precision; -#endif public: explicit knumber_float(const QString &s); explicit knumber_float(double value); #ifdef HAVE_LONG_DOUBLE explicit knumber_float(long double value); #endif - explicit knumber_float(mpf_t mpf); + explicit knumber_float(mpfr_t mpfr); ~knumber_float() override; private: // conversion constructors explicit knumber_float(const knumber_integer *value); explicit knumber_float(const knumber_fraction *value); explicit knumber_float(const knumber_float *value); explicit knumber_float(const knumber_error *value); public: QString toString(int precision) const override; quint64 toUint64() const override; qint64 toInt64() const override; public: bool is_integer() const override; bool is_zero() const override; int sign() const override; public: knumber_base *add(knumber_base *rhs) override; knumber_base *sub(knumber_base *rhs) override; knumber_base *mul(knumber_base *rhs) override; knumber_base *div(knumber_base *rhs) override; knumber_base *mod(knumber_base *rhs) override; public: knumber_base *pow(knumber_base *rhs) override; knumber_base *neg() override; knumber_base *cmp() override; knumber_base *abs() override; knumber_base *sqrt() override; knumber_base *cbrt() override; knumber_base *factorial() override; knumber_base *reciprocal() override; knumber_base *tgamma() override; public: knumber_base *log2() override; knumber_base *log10() override; knumber_base *ln() override; knumber_base *floor() override; knumber_base *ceil() override; knumber_base *exp2() override; knumber_base *exp10() override; knumber_base *exp() override; knumber_base *bin(knumber_base *rhs) override; public: knumber_base *sin() override; knumber_base *cos() override; knumber_base *tan() override; knumber_base *asin() override; knumber_base *acos() override; knumber_base *atan() override; knumber_base *sinh() override; knumber_base *cosh() override; knumber_base *tanh() override; knumber_base *asinh() override; knumber_base *acosh() override; knumber_base *atanh() override; public: int compare(knumber_base *rhs) override; public: knumber_base *bitwise_and(knumber_base *rhs) override; knumber_base *bitwise_xor(knumber_base *rhs) override; knumber_base *bitwise_or(knumber_base *rhs) override; knumber_base *bitwise_shift(knumber_base *rhs) override; public: knumber_base *clone() override; private: - template - knumber_base *execute_libc_func(double x); + knumber_base *ensureIsValid(mpfr_ptr mpfr); + + template + knumber_base *execute_mpfr_func(); + + template + knumber_base *execute_mpfr_func(); + + template + knumber_base *execute_mpfr_func(mpfr_srcptr op); - template - knumber_base *execute_libc_func(double x, double y); + mpfr_ptr new_mpfr(); private: - mpf_t mpf_; + mpfr_t mpfr_; }; } #endif diff --git a/knumber/knumber_integer.cpp b/knumber/knumber_integer.cpp index ad192f4..5a49acf 100644 --- a/knumber/knumber_integer.cpp +++ b/knumber/knumber_integer.cpp @@ -1,888 +1,895 @@ /* Copyright (C) 2001 - 2013 Evan Teran evan.teran@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "knumber_integer.h" #include "knumber_float.h" #include "knumber_fraction.h" #include "knumber_error.h" #include #include namespace detail { //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(const QString &s) { mpz_init(mpz_); mpz_set_str(mpz_, s.toLatin1().constData(), 10); } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(qint32 value) { mpz_init_set_si(mpz_, static_cast(value)); } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(qint64 value) { mpz_init(mpz_); #if SIZEOF_SIGNED_LONG == 8 mpz_set_si(mpz_, static_cast(value)); #elif SIZEOF_SIGNED_LONG == 4 mpz_set_si(mpz_, static_cast(value >> 32)); mpz_mul_2exp(mpz_, mpz_, 32); mpz_add_ui(mpz_, mpz_, static_cast(value)); #else #error "SIZEOF_SIGNED_LONG is a unhandled case" #endif } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(quint32 value) { mpz_init_set_ui(mpz_, static_cast(value)); } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(quint64 value) { mpz_init(mpz_); #if SIZEOF_UNSIGNED_LONG == 8 mpz_set_ui(mpz_, static_cast(value)); #elif SIZEOF_UNSIGNED_LONG == 4 mpz_set_ui(mpz_, static_cast(value >> 32)); mpz_mul_2exp(mpz_, mpz_, 32); mpz_add_ui(mpz_, mpz_, static_cast(value)); #else #error "SIZEOF_UNSIGNED_LONG is a unhandled case" #endif } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(mpz_t mpz) { mpz_init_set(mpz_, mpz); } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(const knumber_integer *value) { mpz_init_set(mpz_, value->mpz_); } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(const knumber_float *value) { mpz_init(mpz_); - mpz_set_f(mpz_, value->mpf_); + + mpf_t mpf; + mpf_init(mpf); + + mpfr_get_f(mpf, value->mpfr_, knumber_float::rounding_mode); + mpz_set_f(mpz_, mpf); + + mpf_clear(mpf); } //------------------------------------------------------------------------------ // Name: knumber_integer //------------------------------------------------------------------------------ knumber_integer::knumber_integer(const knumber_fraction *value) { mpz_init(mpz_); mpz_set_q(mpz_, value->mpq_); } //------------------------------------------------------------------------------ // Name: clone //------------------------------------------------------------------------------ knumber_base *knumber_integer::clone() { return new knumber_integer(this); } //------------------------------------------------------------------------------ // Name: ~knumber_integer //------------------------------------------------------------------------------ knumber_integer::~knumber_integer() { mpz_clear(mpz_); } //------------------------------------------------------------------------------ // Name: add //------------------------------------------------------------------------------ knumber_base *knumber_integer::add(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_add(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *const f = new knumber_float(this); delete this; return f->add(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *const q = new knumber_fraction(this); delete this; return q->add(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { delete this; return p->clone(); } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: sub //------------------------------------------------------------------------------ knumber_base *knumber_integer::sub(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_sub(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->sub(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *q = new knumber_fraction(this); delete this; return q->sub(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { knumber_base *e = p->clone(); delete this; return e->neg(); } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: mul //------------------------------------------------------------------------------ knumber_base *knumber_integer::mul(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_mul(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->mul(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *q = new knumber_fraction(this); delete this; return q->mul(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { if(is_zero()) { delete this; knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); return e->neg(); } if(sign() < 0) { delete this; knumber_base *e = p->clone(); return e->neg(); } else { delete this; return p->clone(); } } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: div //------------------------------------------------------------------------------ knumber_base *knumber_integer::div(knumber_base *rhs) { if(rhs->is_zero()) { if(sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_NEG_INFINITY); } else { delete this; return new knumber_error(knumber_error::ERROR_POS_INFINITY); } } if(knumber_integer *const p = dynamic_cast(rhs)) { knumber_fraction *q = new knumber_fraction(this); delete this; return q->div(p); } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->div(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *q = new knumber_fraction(this); delete this; return q->div(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { if(p->sign() > 0) { delete this; return new knumber_integer(0); } else if(p->sign() < 0) { delete this; return new knumber_integer(0); } delete this; return p->clone(); } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: mod //------------------------------------------------------------------------------ knumber_base *knumber_integer::mod(knumber_base *rhs) { if(rhs->is_zero()) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_mod(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->mod(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *q = new knumber_fraction(this); delete this; return q->mod(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { delete this; return p->clone(); } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: bitwise_and //------------------------------------------------------------------------------ knumber_base *knumber_integer::bitwise_and(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_and(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->bitwise_and(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *f = new knumber_fraction(this); delete this; return f->bitwise_and(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { delete this; return p->clone(); } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: bitwise_xor //------------------------------------------------------------------------------ knumber_base *knumber_integer::bitwise_xor(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_xor(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->bitwise_xor(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *f = new knumber_fraction(this); delete this; return f->bitwise_xor(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { delete this; return p->clone(); } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: bitwise_or //------------------------------------------------------------------------------ knumber_base *knumber_integer::bitwise_or(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_ior(mpz_, mpz_, p->mpz_); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->bitwise_or(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *f = new knumber_fraction(this); delete this; return f->bitwise_or(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { delete this; return p->clone(); } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: bitwise_shift //------------------------------------------------------------------------------ knumber_base *knumber_integer::bitwise_shift(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { const signed long int bit_count = mpz_get_si(p->mpz_); // TODO: left shift with high bit set is broken in // non decimal modes :-/, always displays 0 // interestingly, the bit is not "lost" // we simply don't have a mechanism to display // values in HEX/DEC/OCT mode which are greater than // 64-bits if(bit_count > 0) { // left shift mpz_mul_2exp(mpz_, mpz_, bit_count); } else if(bit_count < 0) { // right shift if(mpz_sgn(mpz_) < 0) { mpz_fdiv_q_2exp(mpz_, mpz_, -bit_count); } else { mpz_tdiv_q_2exp(mpz_, mpz_, -bit_count); } } return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { Q_UNUSED(p); knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); delete this; return e; } else if(knumber_fraction *const p = dynamic_cast(rhs)) { Q_UNUSED(p); knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); delete this; return e; } else if(knumber_error *const p = dynamic_cast(rhs)) { Q_UNUSED(p); knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); delete this; return e; } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: neg //------------------------------------------------------------------------------ knumber_base *knumber_integer::neg() { mpz_neg(mpz_, mpz_); return this; } //------------------------------------------------------------------------------ // Name: cmp //------------------------------------------------------------------------------ knumber_base *knumber_integer::cmp() { #if 0 // unfortunately this breaks things pretty badly // for non-decimal modes :-( mpz_com(mpz_, mpz_); #else mpz_swap(mpz_, knumber_integer(~toUint64()).mpz_); #endif return this; } //------------------------------------------------------------------------------ // Name: abs //------------------------------------------------------------------------------ knumber_base *knumber_integer::abs() { mpz_abs(mpz_, mpz_); return this; } //------------------------------------------------------------------------------ // Name: sqrt //------------------------------------------------------------------------------ knumber_base *knumber_integer::sqrt() { if(sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } if(mpz_perfect_square_p(mpz_)) { mpz_sqrt(mpz_, mpz_); return this; } else { knumber_float *f = new knumber_float(this); delete this; return f->sqrt(); } } //------------------------------------------------------------------------------ // Name: cbrt //------------------------------------------------------------------------------ knumber_base *knumber_integer::cbrt() { mpz_t x; mpz_init_set(x, mpz_); if(mpz_root(x, x, 3)) { mpz_swap(mpz_, x); mpz_clear(x); return this; } mpz_clear(x); knumber_float *f = new knumber_float(this); delete this; return f->cbrt(); } //------------------------------------------------------------------------------ // Name: pow //------------------------------------------------------------------------------ knumber_base *knumber_integer::pow(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { if(is_zero() && p->is_even() && p->sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_POS_INFINITY); } mpz_pow_ui(mpz_, mpz_, mpz_get_ui(p->mpz_)); if(p->sign() < 0) { return reciprocal(); } else { return this; } } else if(knumber_float *const p = dynamic_cast(rhs)) { knumber_float *f = new knumber_float(this); delete this; return f->pow(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { knumber_fraction *f = new knumber_fraction(this); delete this; return f->pow(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { if(p->sign() > 0) { knumber_error *e = new knumber_error(knumber_error::ERROR_POS_INFINITY); delete this; return e; } else if(p->sign() < 0) { mpz_init_set_si(mpz_, 0); return this; } else { knumber_error *e = new knumber_error(knumber_error::ERROR_UNDEFINED); delete this; return e; } } Q_ASSERT(0); return nullptr; } //------------------------------------------------------------------------------ // Name: sin //------------------------------------------------------------------------------ knumber_base *knumber_integer::sin() { knumber_float *f = new knumber_float(this); delete this; return f->sin(); } //------------------------------------------------------------------------------ // Name: cos //------------------------------------------------------------------------------ knumber_base *knumber_integer::cos() { knumber_float *f = new knumber_float(this); delete this; return f->cos(); } //------------------------------------------------------------------------------ // Name: tan //------------------------------------------------------------------------------ knumber_base *knumber_integer::tan() { knumber_float *f = new knumber_float(this); delete this; return f->tan(); } //------------------------------------------------------------------------------ // Name: asin //------------------------------------------------------------------------------ knumber_base *knumber_integer::asin() { knumber_float *f = new knumber_float(this); delete this; return f->asin(); } //------------------------------------------------------------------------------ // Name: acos //------------------------------------------------------------------------------ knumber_base *knumber_integer::acos() { knumber_float *f = new knumber_float(this); delete this; return f->acos(); } //------------------------------------------------------------------------------ // Name: atan //------------------------------------------------------------------------------ knumber_base *knumber_integer::atan() { knumber_float *f = new knumber_float(this); delete this; return f->atan(); } //------------------------------------------------------------------------------ // Name: tgamma //------------------------------------------------------------------------------ knumber_base *knumber_integer::tgamma() { knumber_float *f = new knumber_float(this); delete this; return f->tgamma(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_integer::sinh() { knumber_float *f = new knumber_float(this); delete this; return f->sinh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_integer::cosh() { knumber_float *f = new knumber_float(this); delete this; return f->cosh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_integer::tanh() { knumber_float *f = new knumber_float(this); delete this; return f->tanh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_integer::asinh() { knumber_float *f = new knumber_float(this); delete this; return f->asinh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_integer::acosh() { knumber_float *f = new knumber_float(this); delete this; return f->acosh(); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ knumber_base *knumber_integer::atanh() { knumber_float *f = new knumber_float(this); delete this; return f->atanh(); } //------------------------------------------------------------------------------ // Name: factorial //------------------------------------------------------------------------------ knumber_base *knumber_integer::factorial() { if(sign() < 0) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } mpz_fac_ui(mpz_, mpz_get_ui(mpz_)); return this; } //------------------------------------------------------------------------------ // Name: compare //------------------------------------------------------------------------------ int knumber_integer::compare(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { return mpz_cmp(mpz_, p->mpz_); } else if(knumber_float *const p = dynamic_cast(rhs)) { return knumber_float(this).compare(p); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { return knumber_fraction(this).compare(p); } else if(knumber_error *const p = dynamic_cast(rhs)) { // NOTE: any number compared to NaN/Inf/-Inf always compares less // at the moment return -1; } Q_ASSERT(0); return 0; } //------------------------------------------------------------------------------ // Name: toString //------------------------------------------------------------------------------ QString knumber_integer::toString(int precision) const { Q_UNUSED(precision); const size_t size = gmp_snprintf(nullptr, 0, "%Zd", mpz_) + 1; QScopedArrayPointer buf(new char[size]); gmp_snprintf(&buf[0], size, "%Zd", mpz_); return QLatin1String(&buf[0]); } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ quint64 knumber_integer::toUint64() const { // libgmp doesn't have unsigned long long conversion // so convert to string and then to unsigned long long const QString tmpstring = toString(-1); bool ok; quint64 value; if (sign() < 0) { const qint64 signedvalue = tmpstring.toLongLong(&ok, 10); value = static_cast(signedvalue); } else { value = tmpstring.toULongLong(&ok, 10); } if (!ok) { // TODO: what to do if error? value = 0; } return value; } //------------------------------------------------------------------------------ // Name: //------------------------------------------------------------------------------ qint64 knumber_integer::toInt64() const { // libgmp doesn't have long long conversion // so convert to string and then to long long const QString tmpstring = toString(-1); bool ok; qint64 value = tmpstring.toLongLong(&ok, 10); if (!ok) { // TODO: what to do if error? value = 0; } return value; } //------------------------------------------------------------------------------ // Name: is_integer //------------------------------------------------------------------------------ bool knumber_integer::is_integer() const { return true; } //------------------------------------------------------------------------------ // Name: is_zero //------------------------------------------------------------------------------ bool knumber_integer::is_zero() const { return mpz_sgn(mpz_) == 0; } //------------------------------------------------------------------------------ // Name: sign //------------------------------------------------------------------------------ int knumber_integer::sign() const { return mpz_sgn(mpz_); } //------------------------------------------------------------------------------ // Name: is_even //------------------------------------------------------------------------------ bool knumber_integer::is_even() const { return mpz_even_p(mpz_); } //------------------------------------------------------------------------------ // Name: is_odd //------------------------------------------------------------------------------ bool knumber_integer::is_odd() const { return mpz_odd_p(mpz_); } //------------------------------------------------------------------------------ // Name: reciprocal //------------------------------------------------------------------------------ knumber_base *knumber_integer::reciprocal() { knumber_fraction *q = new knumber_fraction(this); delete this; return q->reciprocal(); } //------------------------------------------------------------------------------ // Name: log2 //------------------------------------------------------------------------------ knumber_base *knumber_integer::log2() { knumber_float *f = new knumber_float(this); delete this; return f->log2(); } //------------------------------------------------------------------------------ // Name: floor //------------------------------------------------------------------------------ knumber_base *knumber_integer::floor() { // should have no effect on the value return this; } //------------------------------------------------------------------------------ // Name: ceil //------------------------------------------------------------------------------ knumber_base *knumber_integer::ceil() { // should have no effect on the value return this; } //------------------------------------------------------------------------------ // Name: log10 //------------------------------------------------------------------------------ knumber_base *knumber_integer::log10() { knumber_float *f = new knumber_float(this); delete this; return f->log10(); } //------------------------------------------------------------------------------ // Name: ln //------------------------------------------------------------------------------ knumber_base *knumber_integer::ln() { knumber_float *f = new knumber_float(this); delete this; return f->ln(); } //------------------------------------------------------------------------------ // Name: exp2 //------------------------------------------------------------------------------ knumber_base *knumber_integer::exp2() { knumber_float *f = new knumber_float(this); delete this; return f->exp2(); } //------------------------------------------------------------------------------ // Name: exp10 //------------------------------------------------------------------------------ knumber_base *knumber_integer::exp10() { knumber_float *f = new knumber_float(this); delete this; return f->exp10(); } //------------------------------------------------------------------------------ // Name: exp //------------------------------------------------------------------------------ knumber_base *knumber_integer::exp() { knumber_float *f = new knumber_float(this); delete this; return f->exp(); } //------------------------------------------------------------------------------ // Name: bin //------------------------------------------------------------------------------ knumber_base *knumber_integer::bin(knumber_base *rhs) { if(knumber_integer *const p = dynamic_cast(rhs)) { mpz_bin_ui(mpz_, mpz_, mpz_get_ui(p->mpz_)); return this; } else if(knumber_float *const p = dynamic_cast(rhs)) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } else if(knumber_fraction *const p = dynamic_cast(rhs)) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } else if(knumber_error *const p = dynamic_cast(rhs)) { delete this; return new knumber_error(knumber_error::ERROR_UNDEFINED); } Q_ASSERT(0); return nullptr; } } diff --git a/knumber/tests/CMakeLists.txt b/knumber/tests/CMakeLists.txt index c961f11..9cc7eb0 100644 --- a/knumber/tests/CMakeLists.txt +++ b/knumber/tests/CMakeLists.txt @@ -1,9 +1,10 @@ include_directories( ${CMAKE_SOURCE_DIR}/knumber ) include(ECMAddTests) +list(APPEND knumbertest_LIBS Qt5::Core ${MPFR_LIBRARIES} ${GMP_LIBRARIES}) + ecm_add_test(knumbertest.cpp ${libknumber_la_SRCS} - LINK_LIBRARIES Qt5::Core ${GMP_LIBRARIES} + LINK_LIBRARIES ${knumbertest_LIBS} TEST_NAME knumbertest ) -