The original incentive for the functions described in this file was to get the ability to decide if a cyclotomic number which happens to be real is positive or negative (this is needed to tell if a root of an arbitrary Coxeter group is negative or positive). Of course, there are other uses for fixed-precision real and complex numbers, which the functions described here provide. A special feature of the present implementation is that to make evaluation of cyclotomics relatively fast, a cache of primitive roots of unity is maintained (the cached values are kept for the largest precision ever used to compute them).
We first describe a general facility to build complex numbers as pairs of real numbers. The real numbers in the pairs can be of any type that GAP3 knows about: integers, rationals, cyclotomics, or elements of any ring actually.
Complex( r[, i] )
In the first form, defines a complex number whose real part is r and imaginary part is i. If omitted, i is taken to be 0. There are two special cases when there is only one argument: if r is already a complex, it is returned; and if r is a cyclotomic number, it is converted to the pair of its real and imaginary part and returned as a complex.
gap> Complex(0,1); I gap> Complex(E(3)); -1/2+ER(3)/2I gap> Complex(E(3)^2); -1/2-ER(3)/2I gap> x:=X(Rationals);;x.name:="x";;Complex(0,x); xI
The last line shows that the arguments to Complex
can be of any ring.
Complex numbers are represented as a record with two fields, .r
and
.i
holding the real and imaginary part respectively.
109.2 Operations for complex numbers
The arithmetic operations +
, -
, *
, /
and ^
work for complex
numbers. They also have Print
and String
methods.
gap> Complex(0,1); I gap> last+1; 1+I gap> last^2; 2I gap> last^2; -4 gap> last+last2; -4+2I gap> x:=X(Rationals);;x.name:="x";;Complex(0,x); xI gap> last^2; -x^2
Finally we should mention the FormatGAP
method, which allows to print
complex numbers in a way such that they can be read back in GAP3:
gap> a:=Complex(1/2,1/3); 1/2+1/3I gap> FormatGAP(a); "Complex(1/2,1/3)"
ComplexConjugate( c)
This function applies complex conjugation to its argument. It
knows ho to do this for cyclotomic numbers (it then just calls
GaloisCyc(c,-1)
), complex numbers, lists (it conjugates each
element of the list), polynomials (it conjugates each coefficient), and
can be taught to conjugate elements x
of an arbitrary domain by
defining x.operations.ComplexConjugate
.
gap> ComplexConjugate(Complex(0,1)); -I gap> ComplexConjugate(E(3)); E(3)^2 gap> x:=X(Cyclotomics);;x.name:="x";;ComplexConjugate(x+E(3)); x + (E(3)^2)
IsComplex( c)
This function returns true
iff its argument is a complex number.
gap> IsComplex(Complex(1,0)); true gap> IsComplex(E(4)); false
evalf( c [, prec] )
The name of this function intentionally mimics that of a Maple
function. It computes a fixed-precision decimal number with prec
digits after the decimal point approximating its argument; if not
given, prec is taken to be 10 (this can be changed via the function
SetDecimalPrecision
, see below). Trailing zeroes are not shown on
output, so the actual precision may be more than the number of digits
shown.
gap> evalf(1/3); 0.3333333333
As one can see, the resulting decimal numbers
have an appropriate
Print method (which uses the String method).
gap> evalf(1/3,20); 0.33333333333333333333
evalf
can also be applied to cyclotomic or complex numbers, yielding
a complex which is a pair of decimal numbers.
gap> evalf(E(3)); -0.5+0.8660254038I
evalf
works also for strings (the result is truncated if too precise)
gap> evalf(".3450000000000000000001"); # precision is 10 0.345
and for lists (it is applied recursively to each element).
gap> evalf([E(5),1/7]); [ 0.3090169944+0.9510565163I, 0.1428571429 ]
Finally, an evalf
method can be defined for elements of any domain.
One has been defined in CHEVIE for complex numbers:
gap> a:=Complex(1/2,1/3); 1/2+1/3I gap> evalf(a); 0.5+0.3333333333I
Rational(d)
d is a decimal
number. The function returns the rational number
which is actually represented by d
gap> evalf(1/3); 0.3333333333 gap> Rational(last); 33333333333/100000000000
SetDecimalPrecision( prec )
This function sets the default precision to be used when converting
numbers to decimal numbers without giving a second argument to evalf
.
gap> SetDecimalPrecision(20); gap> evalf(1/3); 0.33333333333333333333 gap> SetDecimalPrecision(10);
109.8 Operations for decimal numbers
The arithmetic operations +
, -
, *
, /
and ^
work for decimal
numbers, as well as the function GetRoot
and the comparison functions
<
, >
, etc... The precision of the result of an operation is that
of the least precise number used. They can be raised to a fractional
power: GetRoot(d,n)
is equivalent to d^(1/n)
. Decimal numbers
also have Print
and String
methods.
gap> evalf(1/3)+1; 1.3333333333 gap> last^3; 2.3703703704 gap> evalf(E(3)); -0.5+0.8660254038I gap> last^3; 1 gap> evalf(ER(2)); 1.4142135624 gap> GetRoot(evalf(2),2); 1.4142135624 gap> evalf(2)^(1/2); 1.4142135624 gap> evalf(1/3,20); 0.33333333333333333333 gap> last+evalf(1); 1.3333333333 gap> last2+1; 1.33333333333333333333
Finally we should mention the FormatGAP
method, which, given option GAP
,
allows to print decimal numbers in a way such that they can be read back in
GAP3:
gap> FormatGAP(evalf(1/3)); "evalf(33333333333/100000000000,10)"
Pi( [prec])
This function returns a decimal approximation to π, with prec
digits (or if prec is omitted with the default umber of digits
defined by SetDecimalPrecision
, initially 10).
gap> Pi(); 3.1415926536 gap> Pi(34); 3.1415926535897932384626433832795029
Exp( x)
This function returns the complex exponential of x. The argument should be a decimal or a decimal complex. The result has as many digits of precision as the argument.
gap> Exp(evalf(1)); 2.7182818285 gap> Exp(evalf(1,20)); 2.71828182845904523536 gap> Exp(Pi()*E(4)); -1
The code of Exp
shows how easy it is to use decimal numbers.
gap> Print(Exp,"\n"); function ( x ) local res, i, p, z; if IsCyc( x ) then x := evalf( x ); fi; z := 0 * x; res := z; p := 1; i := 1; while p <> z do res := p + res; p := 1 / i * p * x; i := i + 1; od; return res; end
IsDecimal( x)
returns true
iff x is a decimal number.
gap> IsDecimal(evalf(1)); true gap> IsDecimal(evalf(E(3))); false
gap3-jm