Next: , Previous: Symbols, Up: Basic concepts


4.6 Numbers

For storing numerical things, GiNaC uses Bruno Haible's library CLN. The classes therein serve as foundation classes for GiNaC. CLN stands for Class Library for Numbers or alternatively for Common Lisp Numbers. In order to find out more about CLN's internals, the reader is referred to the documentation of that library. see Introduction, for more information. Suffice to say that it is by itself build on top of another library, the GNU Multiple Precision library GMP, which is an extremely fast library for arbitrary long integers and rationals as well as arbitrary precision floating point numbers. It is very commonly used by several popular cryptographic applications. CLN extends GMP by several useful things: First, it introduces the complex number field over either reals (i.e. floating point numbers with arbitrary precision) or rationals. Second, it automatically converts rationals to integers if the denominator is unity and complex numbers to real numbers if the imaginary part vanishes and also correctly treats algebraic functions. Third it provides good implementations of state-of-the-art algorithms for all trigonometric and hyperbolic functions as well as for calculation of some useful constants.

The user can construct an object of class numeric in several ways. The following example shows the four most important constructors. It uses construction from C-integer, construction of fractions from two integers, construction from C-float and construction from a string:

     #include <iostream>
     #include <ginac/ginac.h>
     using namespace GiNaC;
     
     int main()
     {
         numeric two = 2;                      // exact integer 2
         numeric r(2,3);                       // exact fraction 2/3
         numeric e(2.71828);                   // floating point number
         numeric p = "3.14159265358979323846"; // constructor from string
         // Trott's constant in scientific notation:
         numeric trott("1.0841015122311136151E-2");
     
         std::cout << two*p << std::endl;  // floating point 6.283...
         ...

The imaginary unit in GiNaC is a predefined numeric object with the name I:

         ...
         numeric z1 = 2-3*I;                    // exact complex number 2-3i
         numeric z2 = 5.9+1.6*I;                // complex floating point number
     }

It may be tempting to construct fractions by writing numeric r(3/2). This would, however, call C's built-in operator / for integers first and result in a numeric holding a plain integer 1. Never use the operator / on integers unless you know exactly what you are doing! Use the constructor from two integers instead, as shown in the example above. Writing numeric(1)/2 may look funny but works also.

We have seen now the distinction between exact numbers and floating point numbers. Clearly, the user should never have to worry about dynamically created exact numbers, since their `exactness' always determines how they ought to be handled, i.e. how `long' they are. The situation is different for floating point numbers. Their accuracy is controlled by one global variable, called Digits. (For those readers who know about Maple: it behaves very much like Maple's Digits). All objects of class numeric that are constructed from then on will be stored with a precision matching that number of decimal digits:

     #include <iostream>
     #include <ginac/ginac.h>
     using namespace std;
     using namespace GiNaC;
     
     void foo()
     {
         numeric three(3.0), one(1.0);
         numeric x = one/three;
     
         cout << "in " << Digits << " digits:" << endl;
         cout << x << endl;
         cout << Pi.evalf() << endl;
     }
     
     int main()
     {
         foo();
         Digits = 60;
         foo();
         return 0;
     }

The above example prints the following output to screen:

     in 17 digits:
     0.33333333333333333334
     3.1415926535897932385
     in 60 digits:
     0.33333333333333333333333333333333333333333333333333333333333333333334
     3.1415926535897932384626433832795028841971693993751058209749445923078

Note that the last number is not necessarily rounded as you would naively expect it to be rounded in the decimal system. But note also, that in both cases you got a couple of extra digits. This is because numbers are internally stored by CLN as chunks of binary digits in order to match your machine's word size and to not waste precision. Thus, on architectures with different word size, the above output might even differ with regard to actually computed digits.

It should be clear that objects of class numeric should be used for constructing numbers or for doing arithmetic with them. The objects one deals with most of the time are the polymorphic expressions ex.

4.6.1 Tests on numbers

Once you have declared some numbers, assigned them to expressions and done some arithmetic with them it is frequently desired to retrieve some kind of information from them like asking whether that number is integer, rational, real or complex. For those cases GiNaC provides several useful methods. (Internally, they fall back to invocations of certain CLN functions.)

As an example, let's construct some rational number, multiply it with some multiple of its denominator and test what comes out:

     #include <iostream>
     #include <ginac/ginac.h>
     using namespace std;
     using namespace GiNaC;
     
     // some very important constants:
     const numeric twentyone(21);
     const numeric ten(10);
     const numeric five(5);
     
     int main()
     {
         numeric answer = twentyone;
     
         answer /= five;
         cout << answer.is_integer() << endl;  // false, it's 21/5
         answer *= ten;
         cout << answer.is_integer() << endl;  // true, it's 42 now!
     }

Note that the variable answer is constructed here as an integer by numeric's copy constructor, but in an intermediate step it holds a rational number represented as integer numerator and integer denominator. When multiplied by 10, the denominator becomes unity and the result is automatically converted to a pure integer again. Internally, the underlying CLN is responsible for this behavior and we refer the reader to CLN's documentation. Suffice to say that the same behavior applies to complex numbers as well as return values of certain functions. Complex numbers are automatically converted to real numbers if the imaginary part becomes zero. The full set of tests that can be applied is listed in the following table.

Method Returns true if the object is...
.is_zero() ...equal to zero
.is_positive() ...not complex and greater than 0
.is_negative() ...not complex and smaller than 0
.is_integer() ...a (non-complex) integer
.is_pos_integer() ...an integer and greater than 0
.is_nonneg_integer() ...an integer and greater equal 0
.is_even() ...an even integer
.is_odd() ...an odd integer
.is_prime() ...a prime integer (probabilistic primality test)
.is_rational() ...an exact rational number (integers are rational, too)
.is_real() ...a real integer, rational or float (i.e. is not complex)
.is_cinteger() ...a (complex) integer (such as 2-3*I)
.is_crational() ...an exact (complex) rational number (such as 2/3+7/2*I)

4.6.2 Numeric functions

The following functions can be applied to numeric objects and will be evaluated immediately:

Name Function
inverse(z) returns 1/z
pow(a, b) exponentiation a^b
abs(z) absolute value
real(z) real part
imag(z) imaginary part
csgn(z) complex sign (returns an int)
step(x) step function (returns an numeric)
numer(z) numerator of rational or complex rational number
denom(z) denominator of rational or complex rational number
sqrt(z) square root
isqrt(n) integer square root
sin(z) sine
cos(z) cosine
tan(z) tangent
asin(z) inverse sine
acos(z) inverse cosine
atan(z) inverse tangent
atan(y, x) inverse tangent with two arguments
sinh(z) hyperbolic sine
cosh(z) hyperbolic cosine
tanh(z) hyperbolic tangent
asinh(z) inverse hyperbolic sine
acosh(z) inverse hyperbolic cosine
atanh(z) inverse hyperbolic tangent
exp(z) exponential function
log(z) natural logarithm
Li2(z) dilogarithm
zeta(z) Riemann's zeta function
tgamma(z) gamma function
lgamma(z) logarithm of gamma function
psi(z) psi (digamma) function
psi(n, z) derivatives of psi function (polygamma functions)
factorial(n) factorial function n!
doublefactorial(n) double factorial function n!!
binomial(n, k) binomial coefficients
bernoulli(n) Bernoulli numbers
fibonacci(n) Fibonacci numbers
mod(a, b) modulus in positive representation (in the range [0, abs(b)-1] with the sign of b, or zero)
smod(a, b) modulus in symmetric representation (in the range [-iquo(abs(b), 2), iquo(abs(b), 2)])
irem(a, b) integer remainder (has the sign of a, or is zero)
irem(a, b, q) integer remainder and quotient, irem(a, b, q) == a-q*b
iquo(a, b) integer quotient
iquo(a, b, r) integer quotient and remainder, r == a-iquo(a, b)*b
gcd(a, b) greatest common divisor
lcm(a, b) least common multiple

Most of these functions are also available as symbolic functions that can be used in expressions (see Mathematical functions) or, like gcd(), as polynomial algorithms.

4.6.3 Converting numbers

Sometimes it is desirable to convert a numeric object back to a built-in arithmetic type (int, double, etc.). The numeric class provides a couple of methods for this purpose:

     int numeric::to_int() const;
     long numeric::to_long() const;
     double numeric::to_double() const;
     cln::cl_N numeric::to_cl_N() const;

to_int() and to_long() only work when the number they are applied on is an exact integer. Otherwise the program will halt with a message like ‘Not a 32-bit integer’. to_double() applied on a rational number will return a floating-point approximation. Both to_int()/to_long() and to_double() discard the imaginary part of complex numbers.