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 2mpf('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 ampfr_init2(x, 128);// initialise x avec 128 bitsmpfr_init2(a, 128);// initialise ampfr_set_d(a, -1.0, MPFR_RNDN);// affecte -1 à a (a := -1)mpfr_acos(x, a, MPFR_RNDN);// x = acos(a)printf("x: ");// affiche xpfr_out_str(stdout, 10, 0, x, MPFR_RNDN);mpfr_const_pi(x, MPFR_RNDN);// utilise la constante pi de mpfrmpfr_printf("\nx: %.45Rf", x);// analogue à printf de Cmpfr_clear(x);// libere la mémoirempfr_clear(a);}
La compilation se fait avec
gcc test.c -o test -lmpfr -lgmp
On obtient la sortie
x: 3.141592653589793238462643383279502884195x: 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 (output on stream) 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 :
211508817592193908736351888028467202199092987904423346336448512687205539841319467705958443626004842772938273282751463428161461631139866597080252672518426460161763526993536070369012875784140738562164800
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 GPLimport mpmath as mpclass Dct:def __init__(self, data='data'):mp.mp.prec = 80self.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 autrementfor k in range(self.N):s = mp.mpf(0) # sumfor 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π/2Nt = 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)) # orthogonalisationself.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π/2Nt = 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 sif __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 : 80dctdct( 0) = 64318835839289.390625dct( 1) = 11830695203688.791016dct( 2) = 18185603126586.210938dct( 3) = -68102748744173.234375dct( 4) = -31311371966301.996094dct( 5) = -31651701025609.765625dct( 6) = 36033499476967.250000dct( 7) = 37874797111808.484375dct( 8) = 37012142079090.171875dct( 9) = -10074053223196.046875dct(10) = -42547016635737.375000dct(11) = -49927356783822.343750dct(12) = -19516995321046.324219dct(13) = 25438436714700.421875dct(14) = 56154252525608.046875dct(15) = 42911581796127.429688idctu[ 0] = 2115088 ; idct( 0) = 2115088.000000u[ 1] = 17592193908736 ; idct( 1) = 17592193908736.000000u[ 2] = 35188802846720 ; idct( 2) = 35188802846720.000000u[ 3] = 2199092987904 ; idct( 3) = 2199092987904.000000u[ 4] = 423346336448512 ; idct( 4) = 423346336448512.000000u[ 5] = 68720553984 ; idct( 5) = 68720553984.000000u[ 6] = 13194677059584 ; idct( 6) = 13194677059584.000000u[ 7] = 4362600484 ; idct( 7) = 4362600484.000000u[ 8] = 277293827328 ; idct( 8) = 277293827328.000000u[ 9] = 275146342816 ; idct( 9) = 275146342816.000000u[10] = 146163113986 ; idct(10) = 146163113986.000000u[11] = 6597080252672 ; idct(11) = 6597080252672.000000u[12] = 51842646016 ; idct(12) = 51842646016.000000u[13] = 17635269935360 ; idct(13) = 17635269935360.000000u[14] = 70369012875784 ; idct(14) = 70369012875784.000000u[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.cReleased under the GPL (C) 2019 marnout à free pt fr*/#include <stdio.h>#include <string.h>#include <mpfr.h>#define RND MPFR_RNDNconst mpfr_prec_t prec = 80; // précision des nombresconst size_t N = 16; // nombre de donnéesconst 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 nombresmpfr_t u[N], dct[N], s, t;mpfr_inits(s, t, (mpfr_ptr) 0);// read datachar 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 autrementmpfr_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 smpfr_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π/2Nmpfr_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]);} // dctprintf("\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 = 0for(size_t k=0; k<N; k++) { // calcul de smpfr_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π/2Nmpfr_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);} // idctmpfr_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
$ ./dctDCTDCT( 0) = 64318835839289.392138DCT( 1) = 11830695203688.791882DCT( 2) = 18185603126586.209348DCT( 3) = -68102748744173.236873DCT( 4) = -31311371966301.994429DCT( 5) = -31651701025609.764182DCT( 6) = 36033499476967.251851DCT( 7) = 37874797111808.486264DCT( 8) = 37012142079090.175664DCT( 9) = -10074053223196.046458DCT(10) = -42547016635737.375221DCT(11) = -49927356783822.340006DCT(12) = -19516995321046.322296DCT(13) = 25438436714700.421803DCT(14) = 56154252525608.050736DCT(15) = 42911581796127.432834IDCTu[ 0] = 2115088 ; IDCT( 0) = 2115088u[ 1] = 17592193908736 ; IDCT( 1) = 17592193908736u[ 2] = 35188802846720 ; IDCT( 2) = 35188802846720u[ 3] = 2199092987904 ; IDCT( 3) = 2199092987904u[ 4] = 423346336448512 ; IDCT( 4) = 423346336448512u[ 5] = 68720553984 ; IDCT( 5) = 68720553984u[ 6] = 13194677059584 ; IDCT( 6) = 13194677059584u[ 7] = 4362600484 ; IDCT( 7) = 4362600484u[ 8] = 277293827328 ; IDCT( 8) = 277293827328u[ 9] = 275146342816 ; IDCT( 9) = 275146342816u[10] = 146163113986 ; IDCT(10) = 146163113986u[11] = 6597080252672 ; IDCT(11) = 6597080252672u[12] = 51842646016 ; IDCT(12) = 51842646016u[13] = 17635269935360 ; IDCT(13) = 17635269935360u[14] = 70369012875784 ; IDCT(14) = 70369012875784u[15] = 140738562164800 ; IDCT(15) = 140738562164800
On vérifie aussi $u_k=IDCT(k)$