Redondeo correcto de números decimales en código binario

Uno de los problemas serios con los números decimales representados en el código binario es el problema de redondear un número binario al valor de un número decimal representable más cercano a un número decimal correctamente redondeado. A continuación discutimos este problema y damos un algoritmo simple para el redondeo adecuado. El funcionamiento del algoritmo se ilustra mediante un programa de prueba en C ++.

Recuerde que un número decimal representable es un número cuyo valor puede representarse con precisión mediante un código binario. Entonces, el número 0.125 es exactamente igual al número binario 0.001. También se puede argumentar que el valor de cualquier número binario y es igual a algún número decimal representable x . Por ejemplo, el valor del número binario y = 1.001101 * 2 ^ -3 es igual al valor del número decimal representable x = 0.150390625.
Decimos que el número binario y es equivalente al número decimal x . Por el contrario, el número decimal x es equivalente al número binario y .

Veamos qué sucede con un número decimal cuando se ingresa desde la consola o cuando aparece en el programa como una constante.
El compilador convierte (convierte) cualquier número decimal en el formato especificado por el programador. Dado que los números binarios se procesan más rápidamente en una computadora, el número decimal de entrada se convierte, con mayor frecuencia, en un formato de punto fijo (incluido int), o en uno de los formatos de punto flotante: simple, doble u otro formato binario .

De acuerdo con el estándar IEEE754, los números reales en el formato interno de una máquina se representan en forma binaria normalizada. Entonces, un número real binario positivo y se puede representar como y = b0.b1 ... bp * 2 ^ e (b0 ≠ 0).
El mismo número se puede representar como 0.b0b1 ... bp * 2 ^ (e + 1) (b0 ≠ 0) si e + 1> 0 y 0.b0b1 ... bp * 2 ^ e (b0 ≠ 0) si e <0 .
Además nos adheriremos a la segunda vista, ya que En nuestro programa de prueba C ++ a continuación, hay una función q = frexp (x, & e), que nos permite determinar el valor del exponente e en el número binario representado como b0.b1 ... bp * 2 ^ e .

Entonces, el equivalente decimal de cualquier número binario normalizado y = 0.b0b1 ... bp * 2 ^ e (b0 ≠ 0) es igual a algún número decimal normalizado x = 0.d0d1 ... dN ... dn * 10 ^ E (d0 ≠ 0).
Por ejemplo, el número y = 0.1001101 * 2 ^ -2 es equivalente al número decimal representable x = 0.150390625 .
Para obtener el número Xr de x igual al número redondeado a N dígitos significativos, X debe multiplicarse por un factor de 10 ^ k , donde k = NE . Esto es necesario para que el número resultante contenga una parte entera con N dígitos significativos, que luego se puede redondear al entero más cercano. El entero redondeado se debe reducir a la escala anterior multiplicándolo por 10 ^ -k . Matemáticamente, esto se puede escribir con la siguiente fórmula:
X = [x * 10 ^ k + 0.5] * 10 ^ -k = [y * 10 ^ k + 0.5] * 10 ^ -k, donde [] es la parte entera del número.

Se puede demostrar que un número binario entero B que contiene m dígitos binarios es igual al valor del número decimal D , que contiene n = ⌊m * log2⌋ lugares decimales, siempre que B <10 ^ n. E igual a n = n + 1 , siempre que B ≥ 10 ^ n . En los cálculos, podemos tomar log2≈0.301 .

Determinamos el valor de k en función de la información disponible en la representación binaria de y . En la fórmula para k, se conoce la precisión de redondeo de N , por lo que debemos determinar el exponente E.
El exponente E se determina a partir de la relación: E = ⌊e * 0.301⌋ .
Queda por tener en cuenta las siguientes circunstancias. Si x * 10 ^ k = X> 10 ^ N , entonces debe multiplicarlo por 10 ^ (- 1) y ajustar el coeficiente k . Obtenemos X = X * 10 ^ (- 1), k = k-1 .
El número redondeado será igual a Xr = X * 10 ^ (- k) .

Como resultado, obtenemos el siguiente algoritmo simple para el redondeo decimal correcto de números reales binarios. El algoritmo es adecuado para cualquier formato de número binario con una precisión de redondeo decimal N. arbitrariamente especificada
En la entrada:
Precisión de redondeo decimal N ;
Es un número binario en el formato y = 0.b0b1 ... bp * 2 ^ e (b0 ≠ 0) .
Salida: número decimal redondeado X = 0.d0d1 ... dN * 10 ^ E.
- 1. Determine el exponente e del número binario y;
2. E = ⌊e * 0.3⌋;
3. k = NE;
4. X = x * 10 ^ k;
5. Si X <10 ^ N, entonces el ítem 8;
6. X = X * 10 ^ -1;
7. k = k-1;
8. Xr = [X + 0.5] * 10 ^ (- k);
Fin
- En el algoritmo anterior, el número Xr es el número decimal representable más cercano al número, que es el redondeo correcto del número x , que a su vez es el equivalente decimal del número y .
Como estamos acostumbrados a trabajar con números decimales, entonces, como regla, el flujo de entrada es exactamente números decimales. En la salida, también queremos obtener números decimales. Por ejemplo, como en Excel. Pero, después de convertir los números decimales a código binario, generalmente se transforman irreversiblemente. Como resultado de esto, el redondeo convertido a números binarios puede no coincidir con el redondeo correcto de los números impresos en la consola. Esto se aplica principalmente a los casos en que intentamos redondear un número decimal al número máximo posible de dígitos significativos. Para single, esto es 7, y para double es 15.
Esto se puede investigar bien en el programa de prueba a continuación, que el autor escribió en C ++.

En el programa de prueba, previa solicitud, se ingresa lo siguiente en la consola:
- la precisión del redondeo decimal N del número X , que es el número representable más cercano del equivalente binario del número x ;
Es el número x en forma arbitraria.

Debajo del número decimal ingresado x, se imprime el número X , que es el número representable más cercano a x (vea la captura de pantalla a continuación).
El redondeo se realiza en el número X , porque El valor exacto de x se pierde en la conversión binaria.
Devoluciones:
- número decimal en el formato Xr = M * 10 ^ (N + e), donde M es un número decimal entero con N dígitos significativos;
Es el número xr , que es igual al número representable más cercano al equivalente binario normalizado del número X.
imagen
En la captura de pantalla:

N = 15: el número de dígitos decimales significativos a los que se redondea el número decimal de entrada.
x = 7.123456789098765321 e-89 es el número decimal que nos gustaría redondear a 15 dígitos significativos.
X = 7.12345678909876559 e-089: un número decimal representable cuyo valor es igual a un número binario, que se obtiene al convertir el número x al formato p = 53.
Xr = x = 712345678909877e-103 - número entero de mantisa obtenido al redondear el número X.
xr = x = 7.12345678909877e-089 - el número obtenido al normalizar el equivalente binario del número decimal Xr. Es el más cercano a Xr.

A continuación se muestra el código del programa de prueba para el redondeo correcto de los números decimales representados en código binario 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/471506/


All Articles