Carmack's Trick
John Carmack ist ein bekannter Videospielprogrammierer und Mitbegründer von id Software, dem Unternehmen, das für die Entwicklung von Spielen wie Doom, Quake und Wolfenstein 3D bekannt ist.
Carmack ist bekannt für seine technische Expertise und hat eine wichtige Rolle bei der Entwicklung von 3D-Engines für Videospiele gespielt.
Die schnelle Berechnung der inversen Quadratwurzel war für die 3D-Engines von Carmack sehr wichtig, da sie dazu beitrug, die Leistung zu verbessern. Carmack entdeckte diesen Algorithmus während seiner Arbeit an der Spiele-Engine für Quake III Arena und integrierte ihn in den Code.
Dieser Algorithmus wurde später als "Carmack's Trick" bekannt und ist seitdem in vielen Anwendungen weit verbreitet, die eine schnelle Berechnung der inversen Quadratwurzel erfordern.
Es ist interessant zu sehen, wie eine technische Lösung, die für ein Videospiel entwickelt wurde, auch in anderen Bereichen weit verbreitet ist. Carmack's Trick zeigt, wie bedeutend Innovationen in der Videospielbranche auch für andere Bereiche relevant sein können.
Der Algorithmus ermöglicht eine schnellere Berechnung der inversen Quadratwurzel einer Gleitkommazahl. Im Vergleich zur Standardfunktion 1/sqrt()
ist diese Methode um ein Vielfaches schneller und genau genug für viele Anwendungen.
Die Funktion arbeitet wie folgt:
- Der Parameter
num
wird mit 0,5 multipliziert und inx2
gespeichert. - Der Wert von
num
wird iny
gespeichert. - Die Bitdarstellung von
y
wird in die Variablei
konvertiert, die vom Typlong
ist. - Die magische Zahl wird von
i
subtrahiert, nachdem die Hälfte voni
bitweise verschoben wurde. Das Ergebnis wird wieder ini
gespeichert. - Die Bitdarstellung von
i
wird in die Variabley
konvertiert, die vom Typfloat
ist. - Eine Schleife wird für
iterations
-mal ausgeführt, wobeiy
iterativ verbessert wird, um eine genauere Schätzung für die inverse Quadratwurzel zu erhalten.
Die fast_rsqrt
Funktion nimmt zwei Parameter: num
, das die Zahl ist, für die die inverse Quadratwurzel berechnet werden soll, und iterations
, die Anzahl der Iterationen, die durchgeführt werden sollen. Je höher die Anzahl der Iterationen, desto genauer wird das Ergebnis sein.
Die Implementierung der Funktion basiert auf einer "magic number" (0x5f3759df). Diese Zahl wird dazu verwendet, die Bitdarstellung des float-Parameters num zu manipulieren und eine erste Schätzung für die inverse Quadratwurzel zu erhalten. Die weitere Verbesserung erfolgt dann durch das Ausführen mehrerer Iterationen, um das Ergebnis zu verfeinern.
void save(std::string &filename, std::stringstream &data) {
std::ofstream file;
std::string s(data.str());
std::replace(s.begin(), s.end(), '.', ',');
file.open(filename, std::ios::out | std::ios::trunc);
file << s << std::endl;
file.close();
}
int main() {
std::stringstream stream;
float number = 0xffffffffffffffff;
const short iterations = 3;
float duration;
float rsqrt;
LARGE_INTEGER frequency;
LARGE_INTEGER t1, t2;
QueryPerformanceFrequency(&frequency);
stream << "value; ";
for (short i = 1; i <= iterations; i++) {
stream << "result fast_rsqrt("
<< i
<< (i == 1 ? " iteration); " : " iterations); ")
<< "duration fast_rsqrt("
<< i
<< (i == 1 ? " iteration); " : " iterations); ");
}
stream << "result 1 / sqrt; duration 1 /sqrt" << std::endl;
for (short i = 1; i <= 50; i++) {
number /= i;
std::cout << std::endl
<< std::endl
<< " value: "
<< number
<< std::endl
<< "========================"
<< std::endl;
if (number == 0) {
continue;
}
stream << number << "; ";
for (short j = 1; j <= iterations; j++) {
QueryPerformanceCounter(&t1);
rsqrt = fast_rsqrt(number, j);
Sleep(2);
QueryPerformanceCounter(&t2);
duration = (t2.QuadPart - t1.QuadPart - 2) * 1000.0 / frequency.QuadPart;
std::cout << " fast_rsqrt ("
<< j
<< (j == 1 ? " iteration) :\t" : " iterations) :\t")
<< rsqrt << "\texecuted in "
<< duration
<< " ms"
<< std::endl;
stream << rsqrt << "; " << duration << "; ";
}
QueryPerformanceCounter(&t1);
rsqrt = 1 / sqrt(number);
Sleep(2);
QueryPerformanceCounter(&t2);
duration = (t2.QuadPart - t1.QuadPart - 2) * 1000.0 / frequency.QuadPart;
std::cout << " 1 / sqrt :\t"
<< rsqrt
<< "\texecuted in "
<< duration
<< " ms"
<< std::endl << std::endl
<< "------------------------------------------------------------------------------------"
<< std::endl;
stream << rsqrt << "; " << duration << std::endl;
}
for (;;) {
std::string input;
std::cout << std::endl
<< std::endl
<< "---"
<< std::endl
<< "Do you want to export results to CSV? (y/n): ";
std::cin >> input;
if (input.compare("y") == 0 || input.compare("Y") == 0) {
input.clear();
std::string filename("rsqrt-test.csv");
std::cout << std::endl
<< "Filename ("
<< filename
<< "): ";
std::cin >> input;
if (!input.empty()) {
filename = input;
}
save(filename, stream);
break;
} else if (input.compare("n") == 0 || input.compare("N") == 0) {
break;
}
std::cerr << "\n\n"
<< input
<< ":\tI didn't get that. Can you repeat, please?\n\n";
}
return EXIT_SUCCESS;
}
Die Funktion fast_rsqrt
wird im oberen Code Abschnitt mit verschiedenen Werten für iterations
aufgerufen, um zu zeigen, wie sich die Genauigkeit und Ausführungszeit ändern. Schließlich wird die Standardfunktion 1/sqrt()
aufgerufen, um das Ergebnis zu vergleichen.
Carmack's Trick kann für Anwendungen nützlich sein, bei denen eine schnelle Berechnung der inversen Quadratwurzel erforderlich ist, beispielsweise in der Physiksimulation, bei der Grafikberechnung und in der Signalverarbeitung. Es ist jedoch zu beachten, dass diese Methode aufgrund der verwendeten Bitmanipulation nicht für alle Anwendungen geeignet ist. Es ist wichtig, dass der Code vor dem Einsatz sorgfältig getestet und validiert wird, um sicherzustellen, dass er für die spezifische Anwendung geeignet ist.