2016-09-19 2 views
4

Ich habe Code in malloc.c Implementierung gefunden, kann mir bitte jemand sagen was wird dieser Code tatsächlich tun:vereinfachte Version von return ((union {float v; uint32_t r;}) {(int) x} .r >> 21) - 496

return ((union { float v; uint32_t r; }){(int)x}.r>>21) - 496 

ich habe einige Suche und fand, dass es integer zu IEEE 754 Punkt wandelt schwimmend, aber ich bin nicht in der Lage zu verstehen, wie es funktioniert. Kann mir bitte jemand helfen, es in mehreren Schritten zu verstehen?

+0

Wie viel davon verstehen Sie? Ist die Bitverschiebung und die Subtraktion von 496 der Teil, der dich verwirrt? (Ich mag diese Frage, übrigens. Ich bin nicht sicher, ob es am Thema ist, aber ich finde es interessant. :)) –

+1

@student Sie wollen uns sagen, welcher Typ x ist ('size_t', denke ich) und was die Rückkehr Typ der Funktion ist ("Int" wäre meine Vermutung hier). Ansonsten kann Ihre Frage nicht beantwortet werden. – tofro

Antwort

2

Dieser Teil ist ein compound literal:

(union { float v; uint32_t r; }){(int)x} 

Grundsätzlich eine Umwandlung von einer Variablen int zu einem float (dem ersten Elemente der union)

verwendet diese float als uint32_t gegossen wird:

.r 

Und entfernt die las t 21 Bits (Mantisse?):

>>21 

gibt dann den Wert -496

Schritt für Schritt:

union t { 
    float v; 
    uint32_t r; 
}; 

union t u; 

u.v = (int)x; 
u.r >>= 21; 
return u.r - 496; 
+2

Nein, ich denke du hast einen entscheidenden Punkt verpasst. Da die Initialisiererliste '{(int) x}' keinen Mitgliedsbezeichner enthält, initialisiert ihr Element das * erste * Mitglied der Vereinigung, d.h. 'v '. Dies beinhaltet eine Umwandlung von "int" in "float". Das Ergebnis wird dann über das Element 'r' ausgelesen. –

+0

@JohnBollinger du hast Recht, bearbeitet. Besser jetzt? –

+1

Ja, jetzt besser. –

3

Das sieht wie ein Bit-Hack für 4 computing * log x +12 oder etwas, das diesem Wert ziemlich nahe kommt.

Die Idee besteht darin, Hardware zu verwenden, um Binärlogarithmus zu berechnen, und Bitverschiebung zu verwenden, um den Exponentenabschnitt des float zusammen mit zwei oberen Bits der Mantisse zu ernten.

Ich schrieb ein kleines Testprogramm die Berechnungen oben zu vergleichen:

#include <stdio.h> 
#include <stdint.h> 
#include <math.h> 

int makesize(uint32_t x) { 
    return ((union { float v; uint32_t r; }){(int)x}.r>>21) - 496; 
} 

int main(void) { 
    for (uint32_t i = 1 ; i != 1000 ; i++) { 
     double v = i; 
     double x = log(v)/log(2); 
     int y = makesize(i); 
     int res= 12+((int)floor(4*x)); 
     printf("%04d : %d,%d (%d)\n", i, y, res, y-res); 
    } 
    return 0; 
} 

Demo.

Ein Lauf von 1 bis 999 erzeugten Werte innerhalb von 1 voneinander.

+1

Irgendeine Spekulation darüber, warum 'x' in" int "umgewandelt wird, anstatt in einen vorzeichenlosen Typ? Oder in der Tat, warum muss überhaupt gegossen werden? –

+0

@JohnBollinger Ich kenne den Typ von 'x' nicht. Ich habe die Berechnung von OP in eine Funktion eingepackt und gab 'x' den Typ von 'uint32_t', weil ich den Cast in OPs Code verpasst habe. OP bot keine Möglichkeit, den Originalcode zu betrachten, also musste ich über den Typ spekulieren. – dasblinkenlight

+1

Fair genug.Ich beobachte nur, dass (1) der Logarithmus für negative Argumente undefiniert ist, (2) da der Code aus einer 'malloc()' Implementation kommt, ist 'x' wahrscheinlich nicht reell und (3) falls ' x 'hat einen integralen Typ, dann ist der einzige Nettoeffekt der Besetzung, möglicherweise ein implementierungsdefiniertes Verhalten aufzurufen. Es kommt mir aber gerade so vor, dass vielleicht "x" den * pointer * -Typ hat. Das könnte das Casting erklären, wenn auch nicht, warum "int" anstelle eines vorzeichenlosen Integraltyps gewählt wurde. –

Verwandte Themen