Korrekte Rundung von Dezimalzahlen im BinÀrcode

Eines der schwerwiegenden Probleme mit im BinĂ€rcode dargestellten Dezimalzahlen ist das Problem, eine BinĂ€rzahl auf den Wert einer darstellbaren Dezimalzahl zu runden, die einer korrekt gerundeten Dezimalzahl am nĂ€chsten kommt. Im Folgenden diskutieren wir dieses Problem und geben einen einfachen Algorithmus fĂŒr die richtige Rundung an. Die Funktionsweise des Algorithmus wird durch ein Testprogramm in C ++ veranschaulicht.

Denken Sie daran, dass eine darstellbare Dezimalzahl eine Zahl ist, deren Wert durch BinÀrcode genau dargestellt werden kann. Die Zahl 0,125 entspricht also genau der BinÀrzahl 0,001. Es kann auch argumentiert werden, dass der Wert einer beliebigen BinÀrzahl y gleich einer darstellbaren Dezimalzahl x ist . Beispielsweise ist der Wert der BinÀrzahl y = 1,001101 * 2 ^ -3 gleich dem Wert der dezimal darstellbaren Zahl x = 0,150390625.
Wir sagen, dass die BinÀrzahl y der Dezimalzahl x entspricht . Umgekehrt entspricht die Dezimalzahl x der BinÀrzahl y .

Mal sehen, was mit einer Dezimalzahl passiert, wenn sie ĂŒber die Konsole eingegeben wird oder wenn sie im Programm als Konstante angezeigt wird.
Jede Dezimalzahl wird vom Compiler in das vom Programmierer angegebene Format konvertiert (konvertiert). Da BinĂ€rzahlen in einem Computer am schnellsten verarbeitet werden, wird die eingegebene Dezimalzahl meistens entweder in ein Festkommaformat (einschließlich int) oder in eines der Gleitkommaformate - einfach, doppelt oder in ein anderes BinĂ€rformat - konvertiert .

GemĂ€ĂŸ dem IEEE754-Standard werden reelle Zahlen im internen Format einer Maschine in normalisierter BinĂ€rform dargestellt. Eine binĂ€re positive reelle Zahl y kann also als y = b0.b1 ... bp * 2 ^ e (b0 ≠ 0) dargestellt werden.
Die gleiche Zahl kann als 0.b0b1 ... bp * 2 ^ (e + 1) (b0 ≠ 0) dargestellt werden, wenn e + 1> 0 und 0.b0b1 ... bp * 2 ^ e (b0 ≠ 0) wenn e <0 .
Weiter werden wir uns an die zweite Ansicht halten, da In unserem C ++ - Testprogramm unten gibt es eine Funktion q = frexp (x, & e), mit der wir den Wert des Exponenten e in der BinÀrzahl bestimmen können, die als b0.b1 ... bp * 2 ^ e dargestellt wird .

Das DezimalĂ€quivalent einer beliebigen binĂ€ren normalisierten Zahl y = 0.b0b1 ... bp * 2 ^ e (b0 ≠ 0) ist also gleich einer normalisierten Dezimalzahl x = 0.d0d1 ... dN ... dn * 10 ^ E (d0 ≠ 0).
Beispielsweise entspricht die Zahl y = 0,1001101 * 2 ^ -2 der darstellbaren Dezimalzahl x = 0,150390625 .
Um die Zahl Xr von x gleich der auf N signifikanten Stellen gerundeten Zahl zu erhalten, muss X mit einem Faktor von 10 ^ k multipliziert werden, wobei k = NE ist . Dies ist notwendig, damit die resultierende Zahl einen ganzzahligen Teil mit N signifikanten Stellen enthÀlt, der dann auf die nÀchste ganze Zahl gerundet werden kann. Die gerundete ganze Zahl muss dann durch Multiplizieren mit 10 ^ -k auf die vorherige Skala reduziert werden. Mathematisch kann dies mit der folgenden Formel geschrieben werden:
X = [x * 10 ^ k + 0,5] * 10 ^ -k = [y * 10 ^ k + 0,5] * 10 ^ -k, wobei [] der ganzzahlige Teil der Zahl ist.

Es kann gezeigt werden, dass eine ganzzahlige BinĂ€rzahl B mit m BinĂ€rziffern gleich dem Wert der Dezimalzahl D ist , die n = ⌊m * log2⌋ Dezimalstellen enthĂ€lt, vorausgesetzt, B <10 ^ n. Und gleich n = n + 1 , vorausgesetzt B ≄ 10 ^ n . In den Berechnungen können wir log2≈0.301 nehmen.

Wir bestimmen den Wert von k basierend auf den Informationen, die in der binĂ€ren Darstellung von y verfĂŒgbar sind. In der Formel fĂŒr k ist die Rundungsgenauigkeit von N bekannt, daher mĂŒssen wir den Exponenten E bestimmen .
Der Exponent E wird aus der Beziehung bestimmt: E = ⌊e * 0,301⌋ .
Es bleibt der folgende Umstand zu berĂŒcksichtigen. Wenn x * 10 ^ k = X> 10 ^ N ist , mĂŒssen Sie es mit 10 ^ (- 1) multiplizieren und den Koeffizienten k anpassen. Wir erhalten X = X * 10 ^ (- 1), k = k-1 .
Die gerundete Zahl ist gleich Xr = X * 10 ^ (- k) .

Als Ergebnis erhalten wir den folgenden einfachen Algorithmus fĂŒr die korrekte Dezimalrundung von binĂ€ren reellen Zahlen. Der Algorithmus eignet sich fĂŒr jedes BinĂ€rzahlformat mit einer willkĂŒrlich festgelegten Dezimalrundungsgenauigkeit N.
Am Eingang:
Dezimalrundungsgenauigkeit N ;
Ist eine BinĂ€rzahl im Format y = 0.b0b1 ... bp * 2 ^ e (b0 ≠ 0) .
Ausgabe: Abgerundete Dezimalzahl X = 0.d0d1 ... dN * 10 ^ E.
- 1. Bestimmen Sie den Exponenten e der BinÀrzahl y;
2. E = ⌊e * 0,3⌋;
3. k = NE;
4. X = x * 10 ^ k;
5. Wenn X <10 ^ N, dann Punkt 8;
6. X = X * 10 ^ -1;
7. k = k-1;
8. Xr = [X + 0,5] * 10 ^ (- k);
Ende.
- In dem obigen Algorithmus ist die Zahl Xr die darstellbare Dezimalzahl, die der Zahl am nÀchsten liegt. Dies ist die korrekte Rundung der Zahl x , die wiederum das DezimalÀquivalent der Zahl y ist .
Da wir es gewohnt sind, mit Dezimalzahlen zu arbeiten, ist der Eingabestream in der Regel genau Dezimalzahlen. Am Ausgang wollen wir auch Dezimalzahlen erhalten. Zum Beispiel wie in Excel. Nach der Konvertierung von Dezimalzahlen in BinĂ€rcode werden diese jedoch normalerweise irreversibel transformiert. Infolgedessen stimmt die in BinĂ€rzahlen konvertierte Rundung möglicherweise nicht mit der korrekten Rundung der auf der Konsole gedruckten Zahlen ĂŒberein. Dies gilt hauptsĂ€chlich fĂŒr FĂ€lle, in denen versucht wird, eine Dezimalzahl auf die maximal mögliche Anzahl signifikanter Stellen zu runden. FĂŒr Single ist dies 7 und fĂŒr Double ist es 15.
Dies kann mit dem folgenden Testprogramm, das der Autor in C ++ geschrieben hat, gut untersucht werden.

Im Testprogramm wird auf Anfrage Folgendes in die Konsole eingegeben:
- die Genauigkeit der N- Dezimalrundung der Zahl X , die die nĂ€chste darstellbare Zahl des binĂ€ren Äquivalents der Zahl x ist ;
Ist die Zahl x in beliebiger Form.

Unter der eingegebenen Dezimalzahl x wird die Zahl X gedruckt, die die darstellbare Zahl ist, die x am nÀchsten kommt (siehe Abbildung unten).
Die Zahl X wird gerundet, weil Der genaue Wert von x geht bei der binÀren Konvertierung verloren.
RĂŒckgabe:
- Dezimalzahl im Format Xr = M * 10 ^ (N + e), wobei M eine ganzzahlige Dezimalzahl mit N signifikanten Stellen ist;
Ist die Zahl xr , die gleich der darstellbaren Zahl ist, die dem normalisierten binĂ€ren Äquivalent der Zahl X am nĂ€chsten kommt.
Bild
Im Screenshot:

N = 15 - Die Anzahl der dezimalen signifikanten Stellen, auf die die eingegebene Dezimalzahl gerundet ist.
x = 7.123456789098765321 e-89 ist die Dezimalzahl, die wir auf 15 signifikante Stellen runden möchten.
X = 7.12345678909876559 e-089 - eine darstellbare Dezimalzahl, deren Wert gleich der BinÀrzahl ist, die durch Konvertieren der Zahl x in das Format p = 53 erhalten wird.
Xr = x = 712345678909877e-103 - Ganzzahlige Mantissenzahl, erhalten durch Runden der Zahl X.
xr = x = 7.12345678909877e-089 - die Zahl, die durch Normalisieren des binĂ€ren Äquivalents der Dezimalzahl Xr erhalten wird. Es ist am nĂ€chsten an Xr.

Unten finden Sie den Code des Testprogramms fĂŒr die korrekte Rundung von Dezimalzahlen, die im BinĂ€rcode in C ++ dargestellt sind.

#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/de471506/


All Articles