jeudi 28 mars 2019
Pense-bête pour les calculs en multiple précision en python (module mpmath) et en C (GNU MPFR).
En python il faut installer le paquet python-mpmath
.
Utilisation :
>>> from mpmath import *
>>> mpf(2)**mpf(0.5) # racine carrée de 2
mpf('1.4142135623730951')
>>> mp.prec = 128 # precision de 128 bits, soit 38 chiffres en base dix
>>> mpf(2)**mpf(0.5)
mpf('1.414213562373095048801688724209698078569')
mpf
: muliple précision float
mp.prec
: précision en bits
La documentation se trouve à mpmath.org.
En C on installe le paquet mpfr
qui est maintenu par l'Inria.
Utilisation \n
#include <stdio.h>
#include <mpfr.h>
//
void main(void)
{
mpfr_t x, a;// declare x et a
mpfr_init2(x, 128);// initialise x avec 128 bits
mpfr_init2(a, 128);// initialise a
mpfr_set_d(a, -1.0, MPFR_RNDN);// affecte -1 à a (a := -1)
mpfr_acos(x, a, MPFR_RNDN);// x = acos(a)
printf("x: ");// affiche x
pfr_out_str(stdout, 10, 0, x, MPFR_RNDN);
mpfr_const_pi(x, MPFR_RNDN);// utilise la constante pi de mpfr
mpfr_printf("\nx: %.45Rf", x);// analogue à printf de C
mpfr_clear(x);// libere la mémoire
mpfr_clear(a);
}
La compilation se fait avec
gcc test.c -o test -lmpfr -lgmp
On obtient la sortie
x: 3.141592653589793238462643383279502884195
x: 3.141592653589793238462643383279502884195286358
mpfr_t
: multiple precision floating-point reliably type MPFR_RNDN
: mode d'arrondi, ici le plus proche (nearest) mpfr_out_str
: sortie sur le flux (out
put on str
eam) mpfr_printf
est compatible avec printf
de C. Elle ajoute entre autres le type R
pour afficher les mpfr_t
et le type P
pour les mpfr_prec_t
. Documentation : GNU MPFR
On a déjà vu mp.prec
, la précision en bits (binaire)
Il y a aussi mp.dps
(decimal places) pour la précision décimale. Le rapport entre les deux est de 3,33.
On peut modifier temporairement la précision qui est par défaut mp.prec=53
avec mp.prec += 75
puis mp.prec -= 75
.
Il y a aussi workprec
sous forme de directive ou de décorateur. Voir Precision management dans la doc officielle.
Nous avons rencontré mpfr_init2
pour l'initialisation d'une variable. On peut utiliser mpfr_init
(mpfr_t x) qui utilise la précision par défaut (53 bits) ou celle fixée par le programmeur à l'aide de void mpfr_set_default_prec
(mpfr_prec_t prec). Cf. Iniitialisation Functions.
De plus mpfr
utilise plusieurs modes d'arrondis. Voir Rounding Modes
C'est un succédané de la transformation de Fourier. Cette variante n'utilise que les réels et la fonction cosinus. Son intérêt est qu'elle est mieux adaptée à des fonctions non périodiques.
Soit $f$ une fonction définie sur $[0, N-1]$,
pour $k\in \{0, 1, ... , N-1\}$, on note $u_k=f(k)$ .
La tranformée en cosinus de f est
$$dct\left(k\right)=~\frac{2c(k)}{N}~\sum_{n=0}^{N-1}{u_n .cos\frac{(2n+1)k\pi}{2N}}$$
où $c\left(0\right)=\dfrac{1}{\sqrt 2}$ et $c(k)=1$ pour $k\ne 0$
La transformation inverse sera obtenue par
$$idct\left(n\right)=\sum_{k=0}^{N-1}{c(k).dct(k).cos\frac{(2n+1)k\pi}{2N}}$$
Remarque : d'autres variantes existent voir Transformée en cosinus discrète sur Wikipedia ou Interpolation et approximation etc.
Remarque : Cette transformation est utilisée par la compression JPEG.
Pour illustrer notre exemple on prendra la suite de nombre enregistrée dan le fichier data
:
2115088
17592193908736
35188802846720
2199092987904
423346336448512
68720553984
13194677059584
4362600484
277293827328
275146342816
146163113986
6597080252672
51842646016
17635269935360
70369012875784
140738562164800
Nous voudrions afficher les $dct(k)$ puis les $idct(n)$.
Script python avec le module mpmath
:
#!/usr/bin/env python3
# file: dct.py
# (C) 2019 by marnout à free pt fr Released under the GPL
import mpmath as mp
class Dct:
def __init__(self, data='data'):
mp.mp.prec = 80
self.u = list()
with open(data) as f:
for data in f:
self.u.append(mp.mpf(data))
self.N = len(self.u)
@mp.workprec(100)
def _dct(self):
self.dct = list()
# dct type II avec normalisation
# dct(k) = 2γ(k)}/N*Σ_n=0^n=N-1 u[n].cos((2n+1)kπ/2N)
# γ(0) = 1/sqrt(2) et 1 autrement
for k in range(self.N):
s = mp.mpf(0) # sum
for n in range(self.N):
t = mp.fmul(mp.mpf((2*n + 1)*k), mp.pi()) # t = (2n + 1)kπ
t = mp.fdiv(t, self.N*2) # t = (2n + 1)kπ/2N
t = mp.cos(t) # t = cos((2n + 1)kπ/2N)
t = mp.fmul(self.u[n], t) # t = u[n]cos((2n + 1)kπ/2N)
s = mp.fadd(s, t) # Σ
s = mp.fmul(s, 2)
s = mp.fdiv(s, self.N)
if k == 0:
s = mp.fdiv(s, mp.sqrt(2)) # orthogonalisation
self.dct.append(s)
def idct(self, n):
s = mp.mpf(0) # sum
# idct(n) = Σ_k=0^k=N-1 γ(k)dct(k)cos((2n+1)kπ/2N))
for k in range(self.N):
t = mp.fmul(mp.mpf((2*n + 1)*k), mp.pi()) # t = (2n + 1)kπ
t = mp.fdiv(t, 2*self.N) # t = (2n + 1)kπ/2N
t = mp.cos(t) # t = cos((2n + 1)kπ/2N)
t = mp.fmul(self.dct[k], t) # t = dct(k)cos((2n+1)kπ/2N)
if k == 0: t = mp.fdiv(t, mp.sqrt(2)) # Σ
s = mp.fadd(s, t)
# print("k:", k, " n:", n, ' s:', s, file=f)
return s
if __name__ == '__main__':
print(f"\n{__file__} - Mourad Arnout - LGPL\n")
d = Dct()
print("Precision :", mp.mp.prec, '\n')
print("dct")
d._dct()
for k in range(d.N):
print("dct(%2d) = %24.6f" % (k, d.dct[k]))
print("idct")
for k in range(d.N):
print("u[%2d] = %16.0f" % (k, d.u[k]), end=' ; ')
print("idct(%2d) = %24.6f" % (k, d.idct(k)))
print(f"\nfin {__file__}")
Voici dct.py en téléchargement.
Résultat du script :
Precision : 80
dct
dct( 0) = 64318835839289.390625
dct( 1) = 11830695203688.791016
dct( 2) = 18185603126586.210938
dct( 3) = -68102748744173.234375
dct( 4) = -31311371966301.996094
dct( 5) = -31651701025609.765625
dct( 6) = 36033499476967.250000
dct( 7) = 37874797111808.484375
dct( 8) = 37012142079090.171875
dct( 9) = -10074053223196.046875
dct(10) = -42547016635737.375000
dct(11) = -49927356783822.343750
dct(12) = -19516995321046.324219
dct(13) = 25438436714700.421875
dct(14) = 56154252525608.046875
dct(15) = 42911581796127.429688
idct
u[ 0] = 2115088 ; idct( 0) = 2115088.000000
u[ 1] = 17592193908736 ; idct( 1) = 17592193908736.000000
u[ 2] = 35188802846720 ; idct( 2) = 35188802846720.000000
u[ 3] = 2199092987904 ; idct( 3) = 2199092987904.000000
u[ 4] = 423346336448512 ; idct( 4) = 423346336448512.000000
u[ 5] = 68720553984 ; idct( 5) = 68720553984.000000
u[ 6] = 13194677059584 ; idct( 6) = 13194677059584.000000
u[ 7] = 4362600484 ; idct( 7) = 4362600484.000000
u[ 8] = 277293827328 ; idct( 8) = 277293827328.000000
u[ 9] = 275146342816 ; idct( 9) = 275146342816.000000
u[10] = 146163113986 ; idct(10) = 146163113986.000000
u[11] = 6597080252672 ; idct(11) = 6597080252672.000000
u[12] = 51842646016 ; idct(12) = 51842646016.000000
u[13] = 17635269935360 ; idct(13) = 17635269935360.000000
u[14] = 70369012875784 ; idct(14) = 70369012875784.000000
u[15] = 140738562164800 ; idct(15) = 140738562164800.000000
On voit bien que $u_n=idct(n)$.
Le même programme en C utilisant la librairie MPFR
/*
dct.c
Released under the GPL (C) 2019 marnout à free pt fr
*/
#include <stdio.h>
#include <string.h>
#include <mpfr.h>
#define RND MPFR_RNDN
const mpfr_prec_t prec = 80; // précision des nombres
const size_t N = 16; // nombre de données
const size_t wdsize = 20; // taille d'un mot
// int main(int argc, char *argv[])
int main(void)
{
mpfr_set_default_prec(prec); // précision des nombres
mpfr_t u[N], dct[N], s, t;
mpfr_inits(s, t, (mpfr_ptr) 0);
// read data
char data[wdsize];
FILE *fp = fopen("data", "r");
for(size_t k=0; k<N; k++) {
mpfr_init(u[k]);
if(fgets(data, wdsize, fp) == NULL) break;
mpfr_set_str(u[k], data, 0, RND);
}
fclose(fp);
// dct(k) = 2γ(k)}/N*Σ_n=0^n=N-1 u[n].cos((2n+1)kπ/2N)
// γ(0) = 1/sqrt(2) et 1 autrement
mpfr_t sqrt2; mpfr_init(sqrt2); mpfr_sqrt_ui(sqrt2, 2, RND);
printf("DCT\n");
for(size_t k=0; k<N; k++) {
mpfr_init(dct[k]);
mpfr_set_zero(s, 0);
for(size_t n=0; n<N; n++) { // calcul de s
mpfr_const_pi(t, RND); // t = π
mpfr_mul_ui(t, t, (2*n+1)*k, RND); // t = (2n+1)kπ
mpfr_div_ui(t, t, 2*N, RND); // t = (2n+1)kπ/2N
mpfr_cos(t, t, RND); // t = cos((2n+1)kπ/2N)
mpfr_mul(t, u[n], t, RND); // t = u[n]cos((2n+1)kπ/2N)
mpfr_add(s, s, t, RND); // Σ
}
mpfr_mul_ui(s, s, 2, RND);
mpfr_div_ui(s, s, N, RND);
if(k == 0) mpfr_div(s, s, sqrt2, RND);
mpfr_set(dct[k], s, RND);
mpfr_printf("DCT(%2u) = %25Rf\n", k, dct[k]);
} // dct
printf("\nIDCT\n");
// idct(n) = Σ_k=0^k=N-1 γ(k)dct(k)cos((2n+1)kπ/2N))
for(size_t n=0; n<N; n++) {
mpfr_set_zero(s, 0); // s = 0
for(size_t k=0; k<N; k++) { // calcul de s
mpfr_const_pi(t, RND); // t = π
mpfr_mul_ui(t, t, k*(2*n+1), RND); // t = (2n+1)kπ
mpfr_div_ui(t, t, 2*N, RND); // t = (2n+1)kπ/2N
mpfr_cos(t, t, RND); // t = cos((2n+1)kπ/2N)
mpfr_mul(t, t, dct[k], RND); // t = dct(k)cos((2n+1)kπ/2N)
if(k == 0) mpfr_div(t, t, sqrt2, RND);
mpfr_add(s, s, t, RND);
}
mpfr_round(s, s);
mpfr_printf("u[%2d] = %16.0Rf", n, u[n]);
mpfr_printf(" ; IDCT(%2d) = %16.0Rf\n", n, s);
} // idct
mpfr_clears(s, u, t, (mpfr_ptr) 0);
for(size_t k=0; k<N; k++) mpfr_clear(dct[k]);
mpfr_free_cache();
return 0;
}
Voici le fichier dct.c en téléchargement.
Le résultat est semblable au script python
$ ./dct
DCT
DCT( 0) = 64318835839289.392138
DCT( 1) = 11830695203688.791882
DCT( 2) = 18185603126586.209348
DCT( 3) = -68102748744173.236873
DCT( 4) = -31311371966301.994429
DCT( 5) = -31651701025609.764182
DCT( 6) = 36033499476967.251851
DCT( 7) = 37874797111808.486264
DCT( 8) = 37012142079090.175664
DCT( 9) = -10074053223196.046458
DCT(10) = -42547016635737.375221
DCT(11) = -49927356783822.340006
DCT(12) = -19516995321046.322296
DCT(13) = 25438436714700.421803
DCT(14) = 56154252525608.050736
DCT(15) = 42911581796127.432834
IDCT
u[ 0] = 2115088 ; IDCT( 0) = 2115088
u[ 1] = 17592193908736 ; IDCT( 1) = 17592193908736
u[ 2] = 35188802846720 ; IDCT( 2) = 35188802846720
u[ 3] = 2199092987904 ; IDCT( 3) = 2199092987904
u[ 4] = 423346336448512 ; IDCT( 4) = 423346336448512
u[ 5] = 68720553984 ; IDCT( 5) = 68720553984
u[ 6] = 13194677059584 ; IDCT( 6) = 13194677059584
u[ 7] = 4362600484 ; IDCT( 7) = 4362600484
u[ 8] = 277293827328 ; IDCT( 8) = 277293827328
u[ 9] = 275146342816 ; IDCT( 9) = 275146342816
u[10] = 146163113986 ; IDCT(10) = 146163113986
u[11] = 6597080252672 ; IDCT(11) = 6597080252672
u[12] = 51842646016 ; IDCT(12) = 51842646016
u[13] = 17635269935360 ; IDCT(13) = 17635269935360
u[14] = 70369012875784 ; IDCT(14) = 70369012875784
u[15] = 140738562164800 ; IDCT(15) = 140738562164800
On vérifie aussi $u_k=IDCT(k)$