Heute werde ich Ihnen sagen, was Fixpunkt ist, warum er benötigt wird und wie er verwendet werden kann.
Es gibt ein solches Problem, wenn sich die Leistung der Anwendung aufgrund der Besonderheiten von Gleitkommaberechnungen merklich verschlechtern kann. In der Regel wird die CPU für ganzzahlige Operationen geschärft, und der darin enthaltene FPU-Coprozessor (Gleitkommaeinheit) arbeitet um eine Größenordnung langsamer. Es gibt solche Plattformen, auf denen es überhaupt keine FPU gibt und das Emulieren von Operationen mit Zahlen viel Zeit in Anspruch nehmen würde. Beispielsweise wird in Gegenwart einer FPU die Multiplikation von Gleitkommazahlen mit nur einem fmul-Befehl durchgeführt, und in Abwesenheit einer FPU wird die Multiplikation durch die Emulationsfunktion __mulsf3 durchgeführt. Im Vergleich zum Befehl fmul emuliert die Funktion __mulsf3 Operationen mit Gleitkommazahlen, während die Berechnungen in ganzzahliger Form ausgeführt werden, was zu einer Erhöhung des Maschinencodes und der Ausführungszeit führt, während der Befehl fmul diese Operation mithilfe von Hardware schnell ausführt Mittel.
Für dieses Problem gibt es eine Lösung, mit der Berechnungen mit einem festen Punkt für einen ganzzahligen Typ durchgeführt werden können.
Das Prinzip dieses Typs ist eine feste Verschiebung der Zahl um N Bits, wodurch die Bruchzahl als ganze Zahl dargestellt werden kann und nach dem Punkt eine Genauigkeit von 2 ^ N aufweist. Ein Beispiel für die Umwandlung einer Gleitkommazahl in eine Festkommazahl in der Größenordnung von 8 Bit (2 ^ 8 = 1024).
Hier ist ein Beispiel für die Konvertierung einer Gleitkommazahl in eine Festkommazahl:
Fixed(12345,6789) = 1024 * 12345,6789 = 12641975,<s>1936</s>
Diese Zahl nach dem Punkt hat eine Genauigkeit von 2 ^ 8 nach dem Dezimalpunkt.
Ein Beispiel für die umgekehrte Übersetzung einer Festkommazahl in eine Gleitkommazahl.
Float(12641975) = 12641975 / 1024 = 12345,678<s>7109375</s>
In diesem Fall hat die Zahl nach der umgekehrten Übersetzung die Form 12345.6787109375 und ist 3 Stellen nach dem Zeitraum genau. Die maximale Genauigkeit beträgt tatsächlich 2 ^ 8 = 1024.
Wie laufen Berechnungen für einen Festkommatyp ab?
Summen- und Differenzoperationen entsprechen gewöhnlichen ganzzahligen Operationen.
Fixed(x) + Fixed(y) Fixed(x) - Fixed(y)
in beliebiger Reihenfolge
(1024 * x) + (1024 * y) (1024 * x) - (1024 * y)
Die Multiplikation solcher Zahlen erfolgt in dieser Form.
(Fixed(x) * Fixed(y)) / p
, dies entspricht einer Reihenfolge von 8 Bits
((1024 * x) * (1024 * y)) / 1024
Abteilung.
(Fixed(x) * p) / Fixed(y)
, ebenfalls mit einer Reihenfolge von 8 Bit, dies
(1024 * 1024 * x)*(1024 * y)
Überlauf
Bei Multiplikations- und Divisionsoperationen ist ein Überlauf möglich, der zu einem falschen Ergebnis führt. Dies geschieht, wenn beispielsweise ein 32-Bit-Integer-Typ verwendet wird und während der Berechnungen ein Überlauf dieses Typs auftritt und infolge dieses Überlaufs die Zahl die höchstwertigen Bits verliert. Es gibt zwei Möglichkeiten, um einen Überlauf zu vermeiden:
- Führen Sie Berechnungen in einem 64-Bit-Integer-Typ durch.
- Führen Sie Berechnungen in einer "zerlegten" Form durch, z. B. beim Multiplizieren von (xi + xf) * (yi + yf) = xi * yi + xf * yf + xi * yf + yi * xf. Die Präfixe i und f bedeuten den gesamten Teil und den Teil danach Punkte.
Klasse für die Arbeit mit Festkomma in C ++
#define DIGITS 1024 // #define EPS 20 // using namespace std; typedef signed int __int32_t; class Fixed { signed int x; Fixed(signed int a){ x = a; } public: Fixed(){ x = 0; } static Fixed fromInt(signed int val){ return Fixed(val*DIGITS); } static Fixed fromFloat(float val){ return Fixed((signed int)(val*DIGITS)); } float fixed2float(){ return ((float)x)/DIGITS; } Fixed sum(Fixed a,Fixed b){ return Fixed(a.x+bx); } Fixed diff(Fixed a,Fixed b){ return Fixed(ax-bx); } Fixed mul(Fixed a,Fixed b){ signed int c=ax*bx; if(c/bx != ax){ // Overflow! signed int i1 = ax/DIGITS; signed int i2 = bx/DIGITS; signed int f1 = (ax&(DIGITS-1)); signed int f2 = (bx&(DIGITS-1)); return Fixed((i1*i2)*DIGITS+(f1*f2)/DIGITS+i1*f2+i2*f1); }else{ return Fixed(c/DIGITS); } } Fixed div(Fixed a,Fixed b){ if(ax>(1<<21)){ // Overflow! signed int i = ax/DIGITS; signed int f = (ax&(DIGITS-1)); return Fixed(((i*DIGITS)/bx)*DIGITS+(f*DIGITS)/bx); }else{ return Fixed((ax*DIGITS)/bx); } } Fixed sqrt(Fixed k){ Fixed tmp(0); tmp.x = kx/2; signed int min = 0; signed int max = kx; Fixed quick(0); do{ tmp.x = (min+max)/2; quick = Fixed::mul(tmp,tmp); if(abs(quick.xk.x)<EPS) return Fixed(tmp); if(quick.x>kx){ max = tmp.x; }else{ min = tmp.x; } }while(true); } };