#ifndef ABAKUS_NUMERICTYPES_H #define ABAKUS_NUMERICTYPES_H /* * numerictypes.h - part of abakus * Copyright (C) 2004, 2005 Michael Pyne * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "hmath.h" #include "config.h" #if HAVE_MPFR #include #endif namespace Abakus { /* What trigonometric mode we're in. */ typedef enum { Degrees, Radians } TrigMode; /* Shared application-wide */ extern TrigMode m_trigMode; /* Precision to display at. */ extern int m_prec; /** * Representation of a number type. Includes the basic operators, along with * built-in functions such as abs() and mod(). * * You need to actually define it using template specializations though. You * can add functions in a specialization, it may be worth it to have the * functions declared here as well so that you get a compiler error if you * forget to implement it. * * Note that since we're using a specialization, and then typedef'ing the * new specialized class to number_t, that means we only support one type of * number at a time, and the choice is made at compile-time. */ template class number { public: /// Default ctor and set-and-assign ctor wrapped in one. number(const T& t = T()); /// Copy constructor. number(const number &other); /// Create number from textual representation, useful for ginormously /// precise numbers. number(const char *str); /// Convienience constructor to create a number from an integer. explicit number(int i); /// Assignment operator. Be sure to check for &other == this if necessary! number &operator =(const number &other); // You need to implement the suite of comparison operators as well, along // with the negation operator. Sorry. bool operator!=(const number &other) const; bool operator==(const number &other) const; bool operator<(const number &other) const; bool operator>(const number &other) const; bool operator<=(const number &other) const; bool operator>=(const number &other) const; number operator -() const; // These functions must be implemented by all specializations to be used. // Note that when implementing these functions, the implicit value is the // value that this object is wrapping. E.g. you'd call the function on // a number object, kind of like 3.sin() if you were using Ruby. // Trigonometric, must accept values in degrees. number sin() const; number cos() const; number tan() const; // Inverse trigonometric, must return result in Degrees if necessary. number asin() const; number acos() const; number atan() const; // Hyperbolic trigonometric (doesn't use Degrees). number sinh() const; number cosh() const; number tanh() const; // Inverse hyperbolic trigonometric (doesn't use degrees). number asinh() const; number acosh() const; number atanh() const; /// @return Number rounded to closest integer less than or equal to value. number floor() const; /// @return Number rounded to closest integer greater than or equal to value. number ceil() const; /// @return Number with only integer component of result. number integer() const; /// @return Number with only fractional component of result. number frac() const; /** * @return Number rounded to nearest integer. What to do in 'strange' * situations is specialization-dependant, I don't really care enough to * mandate one or the other. */ number round() const; /// @return Absolute value of number. number abs() const; /// @return Square root of number. number sqrt() const; /// @return Natural-base logarithm of value. number ln() const; /// @return base-10 logarithm of value. number log() const; /// @return Natural base raised to the power given by our value. number exp() const; /// @return Our value raised to the \p exponent power. Would be nice if /// it supported even exponents on negative numbers correctly. number pow(const number &exponent); /// @return value rounded to double precision. double asDouble() const; /// @return Textual representation of the number, adjusted to the user's /// current precision. TQString toString() const; /// @return Our value. T value() const; }; // You should also remember to overload the math operators for your // specialization. These generic ones should work for templates wrapping a // type that C++ already has operators for. template inline number operator+(const number &l, const number &r) { return number(l.value() + r.value()); } template inline number operator-(const number &l, const number &r) { return number(l.value() - r.value()); } template inline number operator*(const number &l, const number &r) { return number(l.value() * r.value()); } template inline number operator/(const number &l, const number &r) { return number(l.value() / r.value()); } #if HAVE_MPFR /** * Utility function to convert a MPFR number to a string. This is declared * this way so that when it changes we don't have to recompile all of Abakus. * * This function obeys the precision settings of the user. This means that if * you change the precision between function calls, you may get different * results, even on the same number! * * But, don't use this directly, you should be using * number::toString() instead! * * @param number MPFR number to convert to string. * @return The number converted to a string, in US Decimal format at this time. * @see number<>::toString() */ TQString convertToString(const mpfr_ptr &number); /** * This is a specialization of the number<> template for the MPFR numeric type. * It uses a weird hack in that it is declared as specializing mpfr_ptr instead * of mpfr_t like is used everywhere in MPFR's public API. * * This is because mpfr_t does not seem to play well with C++ templates (it * is implemented internally as a 1-length array to get pointer semantics * while also allocating memory. * * What this means is that should you ever have to deal with allocating * memory, you need to use allocate space for it (mpfr_ptr is a pointer to * __mpfr_struct). * * I don't like using the internal API this way, but I have little choice. * * @author Michael Pyne */ template<> class number { public: typedef mpfr_ptr value_type; static const mp_rnd_t RoundDirection = GMP_RNDN; number(const value_type& t) { m_t = (mpfr_ptr) new __mpfr_struct; mpfr_init_set(m_t, t, RoundDirection); } number(const number &other) { m_t = (mpfr_ptr) new __mpfr_struct; mpfr_init_set(m_t, other.m_t, RoundDirection); } number(const char *str) { m_t = (mpfr_ptr) new __mpfr_struct; mpfr_init_set_str (m_t, str, 10, RoundDirection); } explicit number(int i) { m_t = (mpfr_ptr) new __mpfr_struct; mpfr_init_set_si(m_t, (signed long int) i, RoundDirection); } /// Construct a number with a value of NaN. number() { m_t = (mpfr_ptr) new __mpfr_struct; mpfr_init(m_t); } ~number() { mpfr_clear(m_t); delete (__mpfr_struct *) m_t; } number &operator=(const number &other) { if(&other == this) return *this; mpfr_clear (m_t); mpfr_init_set (m_t, other.m_t, RoundDirection); return *this; } bool operator!=(const number &other) const { return mpfr_equal_p(m_t, other.m_t) == 0; } bool operator==(const number &other) const { return mpfr_equal_p(m_t, other.m_t) != 0; } bool operator<(const number &other) const { return mpfr_less_p(m_t, other.m_t) != 0; } bool operator>(const number &other) const { return mpfr_greater_p(m_t, other.m_t) != 0; } bool operator<=(const number &other) const { return mpfr_lessequal_p(m_t, other.m_t) != 0; } bool operator>=(const number &other) const { return mpfr_greaterequal_p(m_t, other.m_t) != 0; } number operator -() const { number result(m_t); mpfr_neg(result.m_t, result.m_t, RoundDirection); return result; } // internal number asRadians() const { if(m_trigMode == Degrees) { number result(m_t); mpfr_t pi; mpfr_init (pi); mpfr_const_pi (pi, RoundDirection); mpfr_mul (result.m_t, result.m_t, pi, RoundDirection); mpfr_div_ui (result.m_t, result.m_t, 180, RoundDirection); mpfr_clear (pi); return result; } else return m_t; } // internal number toTrig() const { // Assumes num is in radians. if(m_trigMode == Degrees) { number result(m_t); mpfr_t pi; mpfr_init (pi); mpfr_const_pi (pi, RoundDirection); mpfr_mul_ui (result.m_t, result.m_t, 180, RoundDirection); mpfr_div (result.m_t, result.m_t, pi, RoundDirection); mpfr_clear (pi); return result; } else return m_t; } /* There is a lot of boilerplate ahead, so define a macro to declare and * define some functions for us to forward the call to MPFR. */ #define DECLARE_IMPL_BASE(name, func, in, out) number name() const \ { \ number result = in; \ mpfr_##func (result.m_t, result.m_t, RoundDirection); \ \ return out; \ } // Normal function, uses 2 rather than 3 params #define DECLARE_NAMED_IMPL2(name, func) number name() const \ { \ number result = m_t; \ mpfr_##func (result.m_t, result.m_t); \ \ return result; \ } // Normal function, but MPFL uses a different name than abakus. #define DECLARE_NAMED_IMPL(name, func) DECLARE_IMPL_BASE(name, func, m_t, result) // Normal function, just routes call to MPFR. #define DECLARE_IMPL(name) DECLARE_NAMED_IMPL(name, name) // Trig function, degrees in #define DECLARE_TRIG_IN_IMPL(name) DECLARE_IMPL_BASE(name, name, asRadians(), result) // Trig function, degrees out #define DECLARE_TRIG_OUT_IMPL(name) DECLARE_IMPL_BASE(name, name, m_t, result.toTrig()) // Now declare our functions. DECLARE_TRIG_IN_IMPL(sin) DECLARE_TRIG_IN_IMPL(cos) DECLARE_TRIG_IN_IMPL(tan) DECLARE_IMPL(sinh) DECLARE_IMPL(cosh) DECLARE_IMPL(tanh) DECLARE_TRIG_OUT_IMPL(asin) DECLARE_TRIG_OUT_IMPL(acos) DECLARE_TRIG_OUT_IMPL(atan) DECLARE_IMPL(asinh) DECLARE_IMPL(acosh) DECLARE_IMPL(atanh) DECLARE_NAMED_IMPL2(floor, floor) DECLARE_NAMED_IMPL2(ceil, ceil) DECLARE_NAMED_IMPL(integer, rint) DECLARE_IMPL(frac) DECLARE_NAMED_IMPL2(round, round) DECLARE_IMPL(abs) DECLARE_IMPL(sqrt) DECLARE_NAMED_IMPL(ln, log) DECLARE_NAMED_IMPL(log, log10) DECLARE_IMPL(exp) // Can't use macro for this one, it's sorta weird. number pow(const number &exponent) { number result = m_t; mpfr_pow(result.m_t, result.m_t, exponent.m_t, RoundDirection); return result; } double asDouble() const { return mpfr_get_d(m_t, RoundDirection); } // Note that this can be used dangerously, be careful. value_type value() const { return m_t; } TQString toString() const { // Move this to .cpp to avoid recompiling as I fix it. return convertToString(m_t); } static number nan() { // Doesn't apply, but the default value when initialized happens // to be nan. return number(); } static const value_type PI; static const value_type E; private: mpfr_ptr m_t; }; // Specializations of math operators for mpfr. template<> inline number operator+(const number &l, const number &r) { number result; mpfr_add(result.value(), l.value(), r.value(), GMP_RNDN); return result; } template<> inline number operator-(const number &l, const number &r) { number result; mpfr_sub(result.value(), l.value(), r.value(), GMP_RNDN); return result; } template<> inline number operator*(const number &l, const number &r) { number result; mpfr_mul(result.value(), l.value(), r.value(), GMP_RNDN); return result; } template<> inline number operator/(const number &l, const number &r) { number result; mpfr_div(result.value(), l.value(), r.value(), GMP_RNDN); return result; } // Abakus namespace continues. typedef number number_t; #else // Defined in numerictypes.cpp for ease of reimplementation. TQString convertToString(const HNumber &num); /** * Specialization for internal HMath library, used if MPFR isn't usable. * * @author Michael Pyne */ template<> class number { public: typedef HNumber value_type; number(const HNumber& t = HNumber()) : m_t(t) { } explicit number(int i) : m_t(i) { } number(const number &other) : m_t(other.m_t) { } number(const char *s) : m_t(s) { } bool operator!=(const number &other) const { return m_t != other.m_t; } bool operator==(const number &other) const { return m_t == other.m_t; } bool operator<(const number &other) const { return m_t < other.m_t; } bool operator>(const number &other) const { return m_t > other.m_t; } bool operator<=(const number &other) const { return m_t <= other.m_t; } bool operator>=(const number &other) const { return m_t >= other.m_t; } number &operator=(const number &other) { m_t = other.m_t; return *this; } HNumber asRadians() const { if(m_trigMode == Degrees) return m_t * PI / HNumber("180.0"); else return m_t; } HNumber toTrig(const HNumber &num) const { // Assumes num is in radians. if(m_trigMode == Degrees) return num * HNumber("180.0") / PI; else return num; } number sin() const { return HMath::sin(asRadians()); } number cos() const { return HMath::cos(asRadians()); } number tan() const { return HMath::tan(asRadians()); } number asin() const { return toTrig(HMath::asin(m_t)); } number acos() const { return toTrig(HMath::acos(m_t)); } number atan() const { return toTrig(HMath::atan(m_t)); } number floor() const { if(HMath::frac(m_t) == HNumber("0.0")) return integer(); if(HMath::integer(m_t) < HNumber("0.0")) return HMath::integer(m_t) - 1; return integer(); } number ceil() const { return floor().value() + HNumber(1); } /* There is a lot of boilerplate ahead, so define a macro to declare and * define some functions for us to forward the call to HMath. */ #define DECLARE_IMPL(name) number name() const \ { return HMath::name(m_t); } DECLARE_IMPL(frac) DECLARE_IMPL(integer) DECLARE_IMPL(round) DECLARE_IMPL(abs) DECLARE_IMPL(sqrt) DECLARE_IMPL(ln) DECLARE_IMPL(log) DECLARE_IMPL(exp) DECLARE_IMPL(sinh) DECLARE_IMPL(cosh) DECLARE_IMPL(tanh) DECLARE_IMPL(asinh) DECLARE_IMPL(acosh) DECLARE_IMPL(atanh) HNumber value() const { return m_t; } double asDouble() const { return toString().toDouble(); } number operator-() const { return HMath::negate(m_t); } // TODO: I believe this doesn't work for negative numbers with even // exponents. Which breaks simple stuff like (-2)^2. :( number pow(const number &exponent) { return HMath::raise(m_t, exponent.m_t); } TQString toString() const { return convertToString(m_t); } static number nan() { return HNumber::nan(); } static const HNumber PI; static const HNumber E; private: HNumber m_t; }; // Abakus namespace continues. typedef number number_t; #endif /* HAVE_MPFR */ }; // namespace Abakus #endif /* ABAKUS_NUMERICTYPES_H */ // vim: set et ts=8 sw=4: