MPFRCPP Multiple Precision Floating point with correct Rounding, C++ interface ====================================================================== Mpfrcpp is a C++ interface to the Mpfr library. Mpfr, in turn, is a C library of functions and data types to do high precision computations. Mpfr builds on the Gnu MultiPrecision library (Gmp). There are several other C++ interfaces to Mpfr, with different styles and characteristics. See http://www.mpfr.org for a list. 1. GMP ====== Gmp has three fundamental data types, mpz_t - Arbitrary precision signed integers, mpq_t - Rational numers, fractions of two mpz_t, mpf_t - Floating-point numbers where the mantissa is an mpz_t, and offers the operations + - * / sqrt and bit-shifts. Gmp also includes a C++ interface, gmpxx. The main gmpxx datatypes are mpz_class - wrapper around an mpz_t mpq_class - wrapper around an mpq_t mpf_class - wrapper around an mpf_t The floating-point functions of Gmp compute to infinite precision and then truncate the result to the precision of the receiving variable. However, the precision is not strictly controlled, the results can have up to a whole machine word and a few bits more, higher precision than the precision the user has set for the variable. Results differ depending on machine architecture. 2. MPFR ======= Mpfr includes routines to compute various mathematical functions, including exponential and logarithm functions, trigonometric and their inverses, hyperbolic functions, Euler's gamma, ln gamma, Bessel functions, etc. Constants like pi and e are represented by functions in order to have them computed to the desired accuracy. Mpfr introduces a replacement for Gmp's floating-point type mpf_t: mpfr_t - Floating-point numbers of exactly controlled precision and rounding. All functions that operate on mpfr_t have a parameter to control the rounding mode. Results are rounded to the exact precision of the reciving variable, irrespective of machine word boundaries. Mpfr supports signed zeros, infinities, and NaNs. Mpfr semantics is closely related to the IEEE 754 floating-point standard. 3. MPFRCPP ========== Mpfrcpp supports most, but not all features of Mpfr. It is possible to call Mpfr routines directly. The Mpfrcpp representation of mpfr_t is class Real - Wrapper around mpfr_t. Mpfrcpp defines conversions from the Gmp types mpz_t, mpq_t, and mpf_t to Real. There are also conversions from the gmpxx C++ classes. It is possible to use gmpxx together with mpfrcpp, with the former providing wrappers around the mpz_t and mpq_t types. Gmpxx has an elaborate, but hard-to-read system of expression template classes that assists the compiler in generating code for expressions without unnecessary temporaries. On the other hand, expressions are objects of different classes, depending on expression structure. Mpfrcpp can convert the basic gmpxx class objects to Real, but not the expression objects. These must be explicitly converted to mpf_class before passing them to Mpfrcpp functions and expressions. Mpfrcpp has all the usual operator overloadings, including the iostreams operators << and >>. It does not yet have sophisticated expression handling that avoids unnecessary temporaries, as per version 1.4.2. All Mpfrcpp names are in the namespace mpfr. 3.1 Controlling the precision and rounding mode ----------------------------------------------- In a C++ interface we wish to be able to write expressions without having to sprinkle the code with calls to set the precision of each computation. This is achieved in Mpfrcpp using a system of default precisions, a few rules, and means to specify precisions of operations when all else fails. When a precision or rounding mode is supplied in an argument list, it must be wrapped in an object of class Precision or RoundMode. This allows oveloaded functions with any combination of arguments, and compiler knows what to do. There are four predfined RoundMode objects, that should always be used to specify rounding modes: extern const RoundMode roundTowardZero; extern const RoundMode roundToNearest; extern const RoundMode roundTowardInfinity; extern const RoundMode roundTowardNegInfinity; There is a global object called Parameters containing default parameters, including a default precision and rounding mode. The static method Real::getParameters() also returns this object. For each mathematical function there is a function object that stores default precision and rounding mode to be used with that function. It is possible to create additional objects for the same functions, with separate defauls, if one whishes to consider computation to different precisions or rounding modes as different functions. Overloaded operators + - * / and ^ (exponentiation) do not use the corresponding objects Add, Sub, Mul, Div, Pow, but rely on precision propagation and the global defaults (Parameters.*). The initial default precision for all objects is 53 bits. To set the global default precision to e.g., 100 bits: Parameters.setDefaultPrecision(100); To get the default precision as a Precision object: Precision p = Parameters.getDefaultPrecision(); For all other objects, the set/get functions are called set/getPrecision(), not set/getDefaultPrecision. To get the numeric value of a Precision object 'p', do mpfr_prec_t myprec = p.getMpfrPrecT(); 3.2 Precision at initialization ------------------------------- All constructors accepting a Precision object argument, use the specified precision. Other constructors having a first argument of type Real or mpf_class, use the precision of the argument. This includes the copy constructor. A Real variable initialized in any other way, get the global default precision. In particular, notice that initializing from an mpfr_t may lose precision. Also initialization from a string uses the global default precision, which may be insufficient to keep the precision encoded in the string. Examples: mpfr_t X; Real x() // default precision Real y(10) // default precision Real z(X) // default precision Real w(x) // copy constructor - x's precision Real u(x+1) // copy constructor - precision of expression temporary 3.3 Precision in assignment --------------------------- Assigning to a Real variable from another Real, from an mpfr_t, or a mpf_class value, increases the precision of the receiving variable if the source has higher precision. Assigning to a Real variable from any other data type, rounds the value to the precision of the receiving variable. In particular, assigning from a string does not set the precision implicit in the string. There seems to be no reliable way of setting a variable to the full precision of a value that has been written to a string - short of doing your own parsing of the string and setting the receiving variable's precision accordingly. Assigning operators += -= *= /= and ^= are implemented as x = x + y, etc. 3.4 Precision of other expressions ---------------------------- The precision of values produced by functions are described below. The binary operators + - * / and ^ (exponentiation) construct temporaries. If both operands are Real, the temporary gets the precision of the highest precision operand. If only one operand is a Real, the precision of that operand is used. Notice that if an operand is of type mpf_class, it is convertible to Real, and this conversion preserves the precision of the operand. Then the operator will have two Real operands, and the temporary gets the precision of the highest precision operand. 3.5 Mathematical function objects --------------------------------- Mpfrcpp creates a global object for each mathematical function, including the basic arithmetic operations Add, Sub, Div, Mul, and Pow. These objects store the default precision and rounding mode for the corresponding function. The functions that compute constants like e or pi do not have separate objects. They are all members of a special object Constant. Setting the precision on Constant sets the default precision on all its members. The overloaded arithmetic operators do not use the corresponding function objects. They call the underlying mpfr functions directly. The corresponding function can be invoked as, e.g. Real result = Pow(x, y); To specify a default precision for a function object, call, e.g., Pow.setPrecision(150); To specify the precision and/or rounding mode for a single operation, write e.g., result = Pow(x, y, Precision(150)); result = Pow(x, y, roundTowardInfinity); result = Pow(x, y, Precision(150), roundTowardZero); 3.6 Setting multiple defaults at once ------------------------------------- There is a global object called Functions, that contains a list of all global function objects. Setting the precision and/or rounding mode on this object, sets the corresponding attributes on all list elements. See the file core/numeric_function.hpp, class NumericFunctions (note the plural). Example: Functions.setPrecision(200); It is also possible to create your own list objects and include any subset of the available functions, and set the default precision and rounding mode of the elements of this list. See the NumericFunctions class. 3.7 Precision in output ----------------------- Do not confuse mpfr's bit precision with the output precision of printf() and friends or iostreams operators. These operate in digits of the chosen base. To print Real objects with 42 digits after the decimal point (43 digits in all) call cout.precision(42); cout << myReal << endl; 3.8 Conversion ambiguities -------------------------- For most binary operators, the Mpfr library has separate functions that takes an unsigned long int as one of its arguments. When supplying other integral C types to the corresponding Mpfrcpp functions, the C++ compiler may complain about ambiguity. This is avoided using the UL suffix to integral constant arguments. For example, Real x = Pow(2, 1/Real(3)); does not compile, while Real x = Pow(2UL, 1/Real(3)); does compile, as per Mpfrcpp version 1.4.2.