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.
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.
|
The following functions can be applied to numeric objects and will be
evaluated immediately:
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.
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.