Arrondi correct des nombres décimaux dans le code binaire

L'un des problèmes sérieux avec les nombres décimaux représentés dans le code binaire est le problème d'arrondir un nombre binaire à la valeur d'un nombre décimal représentable le plus proche d'un nombre décimal correctement arrondi. Ci-dessous, nous discutons de ce problème et donnons un algorithme simple pour un arrondi correct. Le fonctionnement de l'algorithme est illustré par un programme de test en C ++.

Rappelons qu'un nombre décimal représentable est un nombre dont la valeur peut être représentée avec précision par un code binaire. Ainsi, le nombre 0,125 est exactement égal au nombre binaire 0,001. On peut également affirmer que la valeur de tout nombre binaire y est égale à un nombre décimal représentable x . Par exemple, la valeur du nombre binaire y = 1,001101 * 2 ^ -3 est égale à la valeur du nombre représentable décimal x = 0,150390625.
On dit que le nombre binaire y est équivalent au nombre décimal x . Inversement, le nombre décimal x est équivalent au nombre binaire y .

Voyons ce qui se passe avec un nombre décimal lorsqu'il est entré à partir de la console ou lorsqu'il apparaît dans le programme sous forme de constante.
Tout nombre décimal est converti (converti) par le compilateur dans le format spécifié par le programmeur. Étant donné que les nombres binaires sont traités plus rapidement dans un ordinateur, le nombre décimal d'entrée est converti, le plus souvent, soit dans un format à virgule fixe (y compris int), soit dans l'un des formats à virgule flottante - simple, double ou dans un autre format binaire .

Selon la norme IEEE754, les nombres réels au format interne d'une machine sont représentés sous forme binaire normalisée. Ainsi, un nombre réel positif binaire y peut être représenté comme y = b0.b1 ... bp * 2 ^ e (b0 ≠ 0).
Le même nombre peut être représenté par 0.b0b1 ... bp * 2 ^ (e + 1) (b0 ≠ 0) si e + 1> 0 et 0.b0b1 ... bp * 2 ^ e (b0 ≠ 0) si e <0 .
En outre, nous allons adhérer à la deuxième vue, car dans notre programme de test C ++ ci-dessous, il y a une fonction q = frexp (x, & e), qui nous permet de déterminer la valeur de l'exposant e dans le nombre binaire représenté par b0.b1 ... bp * 2 ^ e .

Ainsi, l'équivalent décimal de tout nombre normalisé binaire y = 0.b0b1 ... bp * 2 ^ e (b0 ≠ 0) est égal à un nombre décimal normalisé x = 0.d0d1 ... dN ... dn * 10 ^ E (d0 ≠ 0).
Par exemple, le nombre y = 0,1001101 * 2 ^ -2 est équivalent au nombre décimal représentable x = 0,150390625 .
Pour obtenir le nombre Xr de x égal au nombre arrondi à N chiffres significatifs, X doit être multiplié par un facteur de 10 ^ k , où k = NE . Ceci est nécessaire pour que le nombre résultant contienne une partie entière avec N chiffres significatifs, qui peuvent ensuite être arrondis à l'entier le plus proche. L'entier arrondi doit ensuite être réduit à l'échelle précédente en le multipliant par 10 ^ -k . Mathématiquement, cela peut être écrit avec la formule suivante:
X = [x * 10 ^ k + 0,5] * 10 ^ -k = [y * 10 ^ k + 0,5] * 10 ^ -k, où [] est la partie entière du nombre.

On peut montrer qu'un nombre binaire entier B contenant m chiffres binaires est égal à la valeur du nombre décimal D , qui contient n = ⌊m * log2⌋ décimales, à condition que B <10 ^ n. Et égal à n = n + 1 , à condition que B ≥ 10 ^ n . Dans les calculs, nous pouvons prendre log2≈0.301 .

Nous déterminons la valeur de k sur la base des informations disponibles dans la représentation binaire de y . Dans la formule pour k, la précision d'arrondi de N est connue, nous devons donc déterminer l'exposant E.
L'exposant E est déterminé à partir de la relation: E = ⌊e * 0,301⌋ .
Il reste à prendre en compte la circonstance suivante. Si x * 10 ^ k = X> 10 ^ N , alors vous devez le multiplier par 10 ^ (- 1) et ajuster le coefficient k . On obtient X = X * 10 ^ (- 1), k = k-1 .
Le nombre arrondi sera égal à Xr = X * 10 ^ (- k) .

Par conséquent, nous obtenons l'algorithme simple suivant pour l'arrondi décimal correct des nombres réels binaires. L'algorithme convient à tout format de nombre binaire avec une précision d'arrondi décimale arbitrairement spécifiée N.
A l'entrée:
Précision d'arrondi décimal N ;
Est un nombre binaire au format y = 0.b0b1 ... bp * 2 ^ e (b0 ≠ 0) .
Sortie: nombre décimal arrondi X = 0.d0d1 ... dN * 10 ^ E.
- 1. Déterminer l'exposant e du nombre binaire y;
2. E = ⌊e * 0,3⌋;
3. k = NE;
4. X = x * 10 ^ k;
5. Si X <10 ^ N, alors le point 8;
6. X = X * 10 ^ -1;
7. k = k-1;
8. Xr = [X + 0,5] * 10 ^ (- k);
Fin.
- Dans l'algorithme ci-dessus, le nombre Xr est le nombre décimal représentable le plus proche du nombre, qui est l'arrondi correct du nombre x , qui à son tour est l'équivalent décimal du nombre y .
Comme nous sommes habitués à travailler avec des nombres décimaux, alors, en règle générale, le flux d'entrée est exactement des nombres décimaux. À la sortie, nous voulons également obtenir des nombres décimaux. Par exemple, comme dans Excel. Mais, après avoir converti les nombres décimaux en code binaire, ils sont généralement transformés de manière irréversible. Par conséquent, l'arrondi converti en nombres binaires peut ne pas coïncider avec l'arrondi correct des nombres imprimés sur la console. Cela s'applique principalement aux cas où nous essayons d'arrondir un nombre décimal au nombre maximum possible de chiffres significatifs. Pour le single, c'est 7 et pour le double c'est 15.
Cela peut être bien étudié sur le programme de test ci-dessous, que l'auteur a écrit en C ++.

Dans le programme de test, sur demande, les informations suivantes sont entrées dans la console:
- la précision de N arrondi décimal du nombre X , qui est le nombre représentable le plus proche de l'équivalent binaire du nombre x ;
Est le nombre x sous forme arbitraire.

Sous le nombre décimal entré x, le nombre X est imprimé, qui est le nombre représentable le plus proche de x (voir capture d'écran ci-dessous).
L'arrondi est effectué sur le nombre X , car la valeur exacte de x est perdue dans la conversion binaire.
Retours:
- nombre décimal au format Xr = M * 10 ^ (N + e),M est un nombre décimal entier avec N chiffres significatifs;
Est le nombre xr , qui est égal au nombre représentable le plus proche de l'équivalent binaire normalisé du nombre X.
image
Dans la capture d'écran:

N = 15 - le nombre de chiffres décimaux significatifs auxquels le nombre décimal d'entrée est arrondi.
x = 7,123456789098765321 e-89 est le nombre décimal que nous aimerions arrondir à 15 chiffres significatifs.
X = 7.12345678909876559 e-089 - un nombre décimal représentable, dont la valeur est égale au nombre binaire obtenu en convertissant le nombre x au format p = 53.
Xr = x = 712345678909877e-103 - nombre entier de mantisse obtenu en arrondissant le nombre X.
xr = x = 7.12345678909877e-089 - le nombre obtenu en normalisant l'équivalent binaire du nombre décimal Xr. C'est le plus proche de Xr.

Ci-dessous se trouve le code du programme de test pour l'arrondi correct des nombres décimaux représentés en code binaire en C ++.

#include <iostream>
#include <math.h>
#include <stdlib.h>
#include <iomanip>
using namespace std;

int main()
{
   double q,x,xr,X;
   unsigned long long int Xr;
   int N,p,E,e,k;

  cout <<"Input a binary precision p=";
  cin>>p;
  cout <<"Input a decimal precision N=";
  cin>>N;
  cout <<endl<<"Input a number and press ENTER:"<<"\n"<<"x= ";
  cin>>x;
   cout<<"X= "<< setprecision(18)<<x << '\n';

    q=frexp (x, &e);
    E =static_cast <int> (e*0.301);
    k=N-E;
   if (E<0)       //for format xr=d0.d1...dN*10^E (d0≠0).
        k=k+1;
    X=x*pow(10,k);
       if (X > pow (10,N)){
            X=X/10;
            k=k-1;
      }

       X=X+0.5;
       Xr=static_cast <unsigned long long int> (X);
       xr=Xr*pow(10,-k);

    cout<<endl <<"Xr= "<<Xr<<"e"<<-k<<'\n';
    cout<<"xr="<<xr<<'\n';

   system("pause");
      return 0;
}


pow(10,k). , k , , 10^k, 5^k.

Source: https://habr.com/ru/post/fr471506/


All Articles