This manual describes how to use the GMP compiler, version 1.0.0 (23 December 2006)
Copyright (C) 2006 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with the Front-Cover Texts being "A GNU Manual", and with the Back-Cover Texts being "You have freedom to copy and modify this GNU Manual, like GNU software".
The gmpc tool simplifies the use of GMP, the GNU multiple precision library. It scans a C source file for specially marked GMPS arithmetic expressions and replaces them with plain C.
The abbreviation gmpc stands for GMP compiler, or alternatively
GMPS-to-C compiler. GMPS arithmetic expressions are straightforward
infix expressions which transparently support the special types
mpq_t
, mpz_t
and mpf_t
as
defined by GMP. GMPS means, rather unimaginatively, `GMP
script'.
No dependencies are added to the resulting C source, so there is no need to include additional header files or link with special libraries other than GMP.
The gmpc distribution can be downloaded here:
On UNIX-like systems, a basic build can be done with
./configure make
The configure
script has a lot of useful options.
Try ./configure --help
to see them. Installation can
be done with
make install
Regression tests are accessible through the check
target.
make check
The tests
directory contains the *.gmpc files under test.
To translate a
.gmpc
file to C source, at least the input and output
files must be given. The most concise invocation would look like
this:
gmpc -o foo.c foo.gmpc
This will translate foo.gmpc into foo.c.
It is highly recommended to enable all warnings with the -Wall switch:
gmpc -Wall -o foo.gmpc foo.c
Other switches can be used to change the default behaviour of gmpc. They are listed in the following sections.
The generated C
code usually contains numerical constants. The output base in which
all mpq_t
, mpz_t
and mpf_t
constants are represented can be set with the -fbase switch. The following example will
cause all constants to be hexadecimal.
gmpc -fbase=16 -o foo.c foo.gmpc
The default base is 10. If you are not interested in generating human-readable code, a power of 2 (e.g. 16) may be more space and time efficient.
The
generated C code may contain mpf_t
constants. The
-ffloat-digits option can
be used to set the number of digits. The following example will set
the number of mpf_t
digits to 120.
gmpc -ffloat-digits=120 -o foo.c foo.gmpc
Note that the number of digits in double
(the
native C type) constants is controlled by the
-fdouble-format
switch.
The
generated C code may contain double
constants. The
-fdouble-format option can
be used to set the formatting of such constants using a
printf
-like format specifier. The following example
will set the number of double
digits to 20, with
trailing zeros removed.
gmpc -fdouble-format=%.20g -o foo.c foo.gmpc
The default value is %24.24e
.
gmpc uses
GMP to evaluate constant expressions. The default precision for
mpf_t
calculations is set with -fprecision. This is the value that is
passed to mpf_set_default_prec
internally. Its default
value is <float digits> * log <base> / log
<2> + 1
.
The generated C code may contain declarations and definitions of temporary variables. In order to avoid name space pollution, a suitable prefix for all symbols can be set with the -fprefix option.
The following example will prefix all symbols with
yy
.
gmpc -fprefix=yy -o foo.c foo.gmpc
The default value is gmps
.
Using a variable before it is defined (using either
import
or var
GMPS statements) will
automatically create it. To guard against typos or the creation of
variables of unexpected types, gmpc has the option
-Wundefined-variables to
show a warning when a variable is created automatically.
This option is implied by the -Wall switch.
In
order to be useful, a .gmpc
file should contain GMPS
statements for at least the following 4 sections:
The option -Wmissing-sections will cause gmpc to emit a warning for each missing section.
This option is implied by the -Wall switch.
Unused
expressions are wasteful, even if they have useful side-effects. In
the following example, the assignment to dr
is carried
out, but the result of +1
is not used.
%% mpf_t 1 + (dr = mpf_sqrt(dx*dx + dy*dy));
If the option -Wunused-expressions is used, the first
+
will trigger a warning.
This option is implied by the -Wall switch.
The option -Wall enables all warnings, i.e. -Wundefined-variables, -Wunused-expressions and -Wmissing-sections.
The
option -fno-linemarkers
inhibits the inclusion of #line xxx
lines in the
generated C code.
The option -fdump-prototypes causes gmpc to list all supported GMP functions, and exit.
The option -version causes gmpc to print its version number on stdout, and exit.
The option -help causes gmpc to print a short help message on stdout, and exit.
GMPS statements are the statements
that are actually seen by gmpc. GMPS statements are embedded in C
code. To distinguish GMPS from C, a GMPS line starts with
%%
. Lines that do not start with this special token
are considered C source, and copied to the output file without any
interpretation.
GMPS lines are usually replaced with generated C source, although sometimes they are just there to tell gmpc something.
Arithmetic expressions can freely mix mpq_t
,
mpz_t
and mpf_t
variables and constants
with the native C types int
, long int
and
unsigned long int
.
There are no control statements like branches and loops in GMPS. These things should be handled in C.
The next sections deal with GMPS in more detail.
Since gmpc ignores C code lines, using C-style comments to comment out sections of code will not prevent gmpc from interpreting GMPS statements.
In the next example, gmpc will still create code for the
init
, body
and cleanup
sections, which end up as C-comments.
int main(void) { /* %% init; foo_func(); %% mpq_t y = (2/3)*x*x + 3*x - 1/12; %% cleanup; */ return 0; }
Changing the comments to #if 0
...
#endif
would have the same effect. GMPS does support
C-style comments, however. The next example does not create
init
, body
and cleanup
sections.
int main(void) { /* %% /* init; foo_func(); %% mpq_t y = (2/3)*x*x + 3*x - 1/12; %% cleanup; */ */ return 0; }
Expressions that are translated
by gmpc typically need temporary variables and constants. gmpc adds
declarations for all temporaries and constants to the declarations
section. In the eventual output, the declarations
keyword is replaced by the full list of C declarations. It can be
placed either at file scope or at function scope, although the
latter only makes sense in C function that contains all 4
obligatory sections declarations
, init
,
body
and cleanup
.
At file scope, a declarations
section looks like
this:
%% declarations; int main(void) { return 0; }
It is also possible to make all C declarations
static
with %% static declarations;
.
Expressions that are translated by gmpc typically need temporary
variables and constants. The init
keyword is replaced
by the necessary C initialisations. It usually consists of a
sequence of GMP calls like mpz_init()
and
mpf_set_str()
.
In GMPS, an init
section looks like this:
%% init;
Your code should be set up so that the generated C code is run exactly one, at startup.
Its counterpart is the cleanup
section.
Expressions that are translated by
gmpc typically need temporary variables and constants. The
cleanup
keyword is replaced by the necessary C cleanup
actions. It usually consists of a sequence of GMP calls like
mpz_clear()
.
In GMPS, a cleanup
section looks like this:
%% cleanup;
If you do not want to leave any allocated memory behind, your code should be set up so that the generated C code is run exactly one, during shutdown.
GMPS has its own name space. If
you want to interface with C code generated by gmpc, you may want
to import variables from your C program into GMPS's name space.
This can be done with the import
keyword.
In the next example, x
and y
are
imported.
void poly(mpq_t y, mpq_t x) { %% import mpq_t x, y; %% mpq_t y = (x+1)*(x-1)*x/6 + 1; }
Note that, since GMPS has only one name space, x
and y
are visible in all subsequent body
sections. In fact, since the import
keyword is not
replaced by C code, it can be placed anywhere in the file, as long
as it comes before the body
section that needs the
variables.
gmpc will not create any code for the declarations
,
init
and cleanup
sections if a variable
is imported – it will just assume that the variable is
available and ready.
Use the var
keyword if you want to leave
initialisation and cleanup to gmpc. For parameters, such as in the
example, import
should be used.
Using a variable before it is
defined will automatically create it. To force the creation of a
variable of a certain type, or to prevent warnings about undefined
variables when -Wundefined-variables is used, a variable
can be defined explicitly with the var
keyword.
In the following example, r2
is defined
explicitly.
void poly(mpq_t y, mpq_t x) { %% import mpq_t x, y; %% var mpq_t r2; %% mpq_t { %% r2 = x*x + y*y; %% y = (x+1)*(x-1)*x/6 + 1; %% } gmp_printf("r2 = %Qd\n", r2); }
This code fragment would also work without the var
statement.
Unlike imported variables, defined variables are logically owned
by GMPS, but available to the C program. gmpc will create code for
the declarations
, init
and
cleanup
sections.
The same is true for variables that are neither defined nor imported.
The body
section is where
most of the work is done. It declares a default type, and contains
one ore more arithmetic expressions that are translated into C
code.
In the following example, the body
section defines
two expressions with a default type mpq_t
.
void poly(mpq_t y, mpq_t x) { %% import mpq_t x, y; %% body mpq_t { %% r2 = x*x + y*y; %% y = (x+1)*(x-1)*x/6 + 1; %% } gmp_printf("r2 = %Qd\n", r2); }
The body
keyword is optional. If only one
expression is defined, the braces can be omitted:
void poly(mpq_t y, mpq_t x) { %% import mpq_t x, y; %% mpq_t y = (x+1)*(x-1)*x/6 + 1; }
Or, since GMPS is a free-format language:
void poly(mpq_t y, mpq_t x) { %% import mpq_t x, y; %% mpq_t %% y = (x+1)*(x-1)*x/6 + 1; }
GMPS expressions are the arithmetic
expressions that are used in body
sections. The
ability to translate them into plain C is the core feature of
gmpc.
Arithmetic expressions are infix, and C-like, although some things are done differently to keep the expressions as clean as possible.
Every expression has a default
type, which is defined by the body
section that
contains it. This type can be mpq_t
,
mpz_t
or mpf_t
.
The main application of the default type is in constants. In the
next example, all constants are mpq_t
constants.
mpq_t q = 1/2 + 1/3 + 1/4;
Default types also play an important part in the creation of undefined variables, and in implicit type conversions.
The type of an undefined variable is determined by the following rules:
0
value.Note 1: Array elements cannot be created automatically. They must be imported.
Note 2: Compound assignments like y +=
x;
are treated as a reference followed by an assignment, so
the default type will be applied if y
is
undefined.
If a type conversion is needed, a C-like cast expression can be used. However, in some cases GMPS allows type mismatches and silently applies conversions.
Furthermore, some operators expect operands of a certain type. Operands will be converted implicitly in the following cases:
<<
and >>
expect
unsigned long int
right-hand side operands. The
resulting type is equal to that of the left-hand side operand.*
, /
, +
and
-
expect both operands to have the same type. An
appropriate mixed-type GMP function may be used, if available. For
example, (mpz_t)a + (unsigned long int)b
will result
in an mpz_add_ui()
function call. If no appropriate
GMP function is found, both operands will be converted to the
default type.<
, <=
,
>
, >=
, ==
and
!=
convert their operands to the default type. If an
appropriate mixed-type GMP function is found, it will be used
instead.&&
, ||
and !
expect int
operands. Their result
is an int
.As an exception, the bitwise operators &
,
|
, ^
and ~
are only defined
for mpz_t
. No conversion is performed. gmpc will abort
with an error message in case of a type mismatch.
gmpc
recognises constants that are accepted by the underlying GMP
functions mpX_set_str()
with a base
argument of 0
.
For mpq_t
and mpz_t
, decimal constants
are accepted. Furthermore, gmpc handles prefixes for hex
(0x
or 0X
), octal (0
) and
binary (0b
or 0B
).
For mpf_t
only decimal constants are accepted.
For more exotic bases, the following syntax can be used:
/* add a base 36 constant */ %% mpz_t x2 = x1 + (mpz_t) const ("ag12", 36);
The string and base arguments are passed directly to
mpX_set_str()
. The type is derived from a preceding
cast if present, otherwise the default type is used (so in the
previous example, the cast is not really needed). Note that this is
the only way to introduce mpf_t
or double
constants in an mpq_t
or mpz_t
body.
GMPS supports GMP function calls. Use
gmpc -fdump-prototypes
to see the complete list of
supported functions. This list should contain all functions that
can be called using the types that GMPS supports:
mpq_t
, mpz_t
, mpf_t
,
double
, unsigned long int
, long
int
and int
(void
return values
are treated specially). This excludes functions like void
mpz_random (mpz_t rop, mp_size_t max_size)
, because GMPS
does not know how to handle the mp_size_t
parameter.
GMP functions without a return value behave as if they do have a
return value. The void
return value is replaced with
the first parameter. This makes them easier to use in
expressions.
r_new = mpf_sqrt(r, x*x + y*y) + r_old;
Here, the square root is stored in r
and also used
in the addition.
Furthermore, it is allowed to omit the first parameter
altogether, if it is pass-by-reference. GMPS implicitly assumes it
is an output variable. So in the previous example, if
r
is not needed, the following would be
equivalent:
r_new = mpf_sqrt(x*x + y*y) + r_old;
GMPS does not treat arrays specially. Variable references may have trailing array index expressions. They are simply copied to the output file.
In this example, it is assumed that the arrays A
,
B
and C
have been set up beforehand. The
[i]
subscripts will be copied verbatim to the
output.
%% import mpq_t A, B, C; for (i=0; i<N; i++) { %% mpq_t %% A[i] = f * B[i] + (1-f) * C[i]; }
Arrays must be imported, and since gmpc does not know anything
about array dimensions, they must be imported as scalars. In GMPS,
A[1]
and A[1][i+j]
have the same type as
A
.
Note: GMPS does not support nested
[]
brackets, so things like A[c[i,j]][k]
will be rejected by gmpc.
In GMPS the operator precedence is the
same as in C. This means that in many cases the evaluation order is
predictable, at least to a degree that makes sense mathematically.
So a+b*c
is equivalent to a+(b*c)
, or
maybe (c*b)+a
but never (a+b)*c
.
Especially if a
, b
and c
have different types, they may be shuffled around to better fit the
available GMP functions.
Increment and decrement operations are treated as side effects, which are guaranteed to have been fully evaluated at certain points in an expression. In GMPS these points are:
&&
and ||
.This is essentially the subset of C sequence points that are relevant to GMPS.
This
section shows an annotated example of a sine function. The source
file, sine.gmpc, can be
found in the tests
directory of the gmpc
distribution.
First, the usual #include
statements are
needed.
#include <stdio.h> #include <gmp.h> %% static declarations;
We want gmpc to create static declarations, directly after the
#include
statements, and before the definition of the
mpf_sine_raw
function.
/* raw sine, first quadrant only, using Taylor */ void mpf_sine_raw(mpf_t sum, mpf_t x) { %% import mpf_t sum, x;
The function parameters must be imported, since we do not want
gmpc to create init
and cleanup
code for
them.
long int n_narrowing = 3; long int i; long int i_fact = 1; long int sign = 1; %% import long int i_fact, sign;
We will manipulate these variables in C, so we define them in C and then import the variables that are used in the formulas into GMPS.
int stop = 0; %% import int stop; %% mpf_t abs_tol = x * 1e-50;
stop
should be a boolean with function scope, so we
declare it in our C source and then import it. abs_tol
is not defined, so gmpc will create it for us, with file scope.
/* divide angle by 5 a few times, undo later using */ /* sin(5*x) = 16*sin^5(x)-20*sin^3(x)+5*sin(x) */ for (i=0; i<n_narrowing; i++) { %% mpf_t { %% x *= 1/5; %% abs_tol *= 1/5; %% } } /* init Taylor, i = 1 */ %% mpf_t { %% sum = x; %% xi = x; %% x2 = x * x; %% }
We introduce some more undefined variables, which will be taken care of by gmpc.
/* Taylor series expansion, i = 3, 5, 7, ... */ for (i=3; !stop; i += 2) { sign *= -1; i_fact *= (i-1) * i; %% mpf_t { %% xi *= x2; %% term = sign * xi / i_fact; %% sum += term; %% stop = mpf_abs(term) < abs_tol; %% } } /* Blow up angle again */ for (i=0; i<n_narrowing; i++) { %% mpf_t { %% sum2 = sum * sum; %% sum *= 5 + sum2 * (-20 + 16 * sum2); %% } } }
We need a main
to test our function.
int main(void) { mpf_set_default_prec (100); %% init;
Initialisation code will be inserted here.
%% mpf_t { test_sine = 0; test_arg = 0.5; }
Again, let gmpc handle these variables.
gmp_printf("mpf_sine_raw(%.Ff) = ", test_arg); mpf_sine_raw(test_sine, test_arg); gmp_printf("%.Ff\n", test_sine); return 0; }
This completes sine.gmpc. gmpc is invoked as follows:
gmpc -Wall -o sine.c sine.gmpc
After a lot of warnings, a file sine.c is produced.
sine.gmpc:18:14: warning: undefined variable 'abs_tol' sine.gmpc:34:12: warning: undefined variable 'xi' sine.gmpc:35:12: warning: undefined variable 'x2' sine.gmpc:45:16: warning: undefined variable 'term' sine.gmpc:55:16: warning: undefined variable 'sum2' sine.gmpc:65:16: warning: undefined variable 'test_sine' sine.gmpc:65:31: warning: undefined variable 'test_arg' sine.gmpc: warning: missing cleanup section
The resulting sine.c can be compiled:
gcc -lgmp sine.c
And the result of all this is:
./a.out mpf_sine_raw(0.5) = 0.4794255386042030002732879352155713880842
For the sake of completeness, here is the generated sine.c:
#line 1 "sine.gmpc" #include <stdio.h> #include <gmp.h> #line 6 "sine_example.c" static mpf_t abs_tol; static mpf_t gmpscf000; static mpf_t gmpscf001; static mpf_t gmpscf002; static mpf_t gmpscf003; static mpf_t gmpscf004; static mpf_t gmpscf005; static mpf_t gmpscf006; static mpf_t gmpstf000; static mpf_t sum2; static mpf_t term; static mpf_t test_arg; static mpf_t test_sine; static mpf_t x2; static mpf_t xi; #line 5 "sine.gmpc" /* raw sine, first quadrant only, using Taylor */ void mpf_sine_raw(mpf_t sum, mpf_t x) { long int n_narrowing = 3; long int i; long int i_fact = 1; long int sign = 1; int stop = 0; #line 33 "sine_example.c" mpf_mul (abs_tol, x, gmpscf000); #line 19 "sine.gmpc" /* divide angle by 5 a few times, undo later using */ /* sin(5*x) = 16*sin^5(x)-20*sin^3(x)+5*sin(x) */ for (i=0; i<n_narrowing; i++) { #line 41 "sine_example.c" mpf_mul (x, x, gmpscf001); mpf_mul (abs_tol, abs_tol, gmpscf001); #line 28 "sine.gmpc" } /* init Taylor, i = 1 */ #line 49 "sine_example.c" mpf_set (sum, x); mpf_set (xi, x); mpf_mul (x2, x, x); #line 37 "sine.gmpc" /* Taylor series expansion, i = 3, 5, 7, ... */ for (i=3; !stop; i += 2) { sign *= -1; i_fact *= (i-1) * i; #line 60 "sine_example.c" mpf_mul (xi, xi, x2); mpf_set_si (term, sign); mpf_mul (term, xi, term); mpf_set_si (gmpstf000, i_fact); mpf_div (term, term, gmpstf000); mpf_add (sum, sum, term); mpf_abs (gmpstf000, term); stop = mpf_cmp (gmpstf000, abs_tol); stop = ((stop) < (0)); #line 49 "sine.gmpc" } /* Blow up angle again */ for (i=0; i<n_narrowing; i++) { #line 76 "sine_example.c" mpf_mul (sum2, sum, sum); mpf_mul (gmpstf000, gmpscf002, sum2); mpf_add (gmpstf000, gmpscf003, gmpstf000); mpf_mul (gmpstf000, sum2, gmpstf000); mpf_add (gmpstf000, gmpscf004, gmpstf000); mpf_mul (sum, sum, gmpstf000); #line 58 "sine.gmpc" } } int main(void) { mpf_set_default_prec (100); #line 90 "sine_example.c" mpf_init (abs_tol); mpf_init (gmpscf000); mpf_set_str (gmpscf000, "0.1e-49", 10); mpf_init (gmpscf001); mpf_set_str (gmpscf001, "0.2e0", 10); mpf_init (gmpscf002); mpf_set_str (gmpscf002, "0.16e2", 10); mpf_init (gmpscf003); mpf_set_str (gmpscf003, "-0.2e2", 10); mpf_init (gmpscf004); mpf_set_str (gmpscf004, "0.5e1", 10); mpf_init (gmpscf005); mpf_set_str (gmpscf005, "0.e0", 10); mpf_init (gmpscf006); mpf_set_str (gmpscf006, "0.5e0", 10); mpf_init (gmpstf000); mpf_init (sum2); mpf_init (term); mpf_init (test_arg); mpf_init (test_sine); mpf_init (x2); mpf_init (xi); mpf_set (test_sine, gmpscf005); mpf_set (test_arg, gmpscf006); #line 66 "sine.gmpc" gmp_printf("mpf_sine_raw(%.Ff) = ", test_arg); mpf_sine_raw(test_sine, test_arg); gmp_printf("%.Ff\n", test_sine); return 0; }
Note how gmpc inserts #line
statements, so C
compilers and debuggers can refer directly to the
.gmpc file.
program: statement_list <<EOF>> | <<EOF>> ; statement_list: statement | statement_list statement ; statement: "init" ";" | decl_statement ";" | var_statement ";" | body_statement | "cleanup" ";" | {CSOURCE} ; decl_statement: "declarations" | "static" "declarations" ; var_statement: "import" any_type ident_list | "var" any_type ident_list ; ident_list: {IDENT} | ident_list "," {IDENT} ; body_statement: "body" any_type exp_arg | any_type exp_arg ; exp_arg: exp ";" | "{" exp_list "}" | "{" exp_list ";" "}" ; exp_list: exp | exp_list ";" exp ; arg_list: exp | arg_list "," exp ; exp: {OCTINT}|{FLOAT}|{HEXINT}|{BININT} | ident | exp "+" exp | exp "-" exp | exp "*" exp | exp "/" exp | exp "%" exp | exp "<<" exp | exp ">>" exp | exp "==" exp | exp "!=" exp | exp "<" exp | exp "<=" exp | exp ">" exp | exp ">=" exp | exp "&&" exp | exp "||" exp | exp "&" exp | exp "|" exp | exp "^" exp | "-" exp | "+" exp | "!" exp | "~" exp | "(" exp ")" | exp "=" exp | exp "<<="|">>="|[\-%+/*&^|~]"=" exp | "++" exp | "--" exp | exp "++" | exp "--" | {IDENT} "(" arg_list ")" | cast exp | const_escape ; cast: "(" any_type ")" ; any_type: long_int | "signed" | "signed" long_int | "unsigned" | "unsigned" long_int | "double" | "mpq_t" | "mpz_t" | "mpf_t" | "int" ; long_int: "long" | "long" "int" ; const_escape: "const" "(" \"[^"]*\" "," {OCTINT}|{FLOAT}|{HEXINT}|{BININT} ")" ; ident: {IDENT} | {IDENT} array_index_list ; array_index_list: "["[^]]*"]" | array_index_list "["[^]]*"]" ;