Als ich das 21. Jahrhundert C las , kam ich zu Kapitel 6 im Abschnitt "Markieren außergewöhnlicher numerischer Werte mit NaNs" , wo die Verwendung der Bits in der Mantisse zum Speichern von willkürlichen Bitmustern und deren Verwendung als Marker oder Zeiger erläutert wird (das Buch erwähnt dies) dass WebKit diese Technik verwendet).
Ich bin mir nicht sicher, ob ich die Nützlichkeit dieser Technik verstanden habe, die ich als Hack verstehe (sie beruht darauf, dass sich die Hardware nicht um den Wert der Mantisse in einem NaN kümmert), aber ich komme aus einem Java-Hintergrund, an den ich nicht gewöhnt bin die Rauheit von C.
Hier ist das Codefragment, mit dem ein Marker in einer NaN gesetzt und gelesen wird
#include <stdio.h>
#include <math.h> //isnan
double ref;
double set_na(){
if (!ref) {
ref=0/0.;
char *cr = (char *)(&ref);
cr[2]='a';
}
return ref;
}
int is_na(double in){
if (!ref) return 0; //set_na was never called==>no NAs yet.
char *cc = (char *)(&in);
char *cr = (char *)(&ref);
for (int i=0; i< sizeof(double); i++)
if (cc[i] != cr[i]) return 0;
return 1;
}
int main(){
double x = set_na();
double y = x;
printf("Is x=set_na() NA? %i\n", is_na(x));
printf("Is x=set_na() NAN? %i\n", isnan(x));
printf("Is y=x NA? %i\n", is_na(y));
printf("Is 0/0 NA? %i\n", is_na(0/0.));
printf("Is 8 NA? %i\n", is_na(8));
}
es druckt:
Is x=set_na() NA? 1
Is x=set_na() NAN? 1
Is y=x NA? 1
Is 0/0 NA? 0
Is 8 NA? 0
und bei JSValue.h erklärt das Webkit die Codierung, aber nicht, warum sie verwendet wird.
Was ist der Zweck dieser Technik? Sind die Vorteile von Speicherplatz / Leistung hoch genug, um seine hackige Natur auszugleichen?
quelle
Antworten:
Wenn Sie eine dynamisch typisierte Sprache implementieren, müssen Sie einen einzigen Typ haben, der alle Ihre Objekte aufnehmen kann. Dafür gibt es drei verschiedene Ansätze, die mir bekannt sind:
Erstens können Sie Zeiger weitergeben. Das macht die CPython-Implementierung. Jedes Objekt ist ein
PyObject
Zeiger. Diese Zeiger werden weitergegeben, und Operationen werden ausgeführt, indem Details in der PyObject-Struktur betrachtet werden, um den Typ zu ermitteln.Der Nachteil ist, dass kleine Werte wie Zahlen als Box-Werte gespeichert werden. So wird Ihre kleine 5 irgendwo als Speicherblock gespeichert. Das führt uns also zu dem von Lua verwendeten Unionsansatz. Anstelle von a
PyObject*
ist jeder Wert eine Struktur, in der ein Feld den Typ angibt, und dann eine Vereinigung aller verschiedenen unterstützten Typen. Auf diese Weise vermeiden wir, kleinen Werten Speicher zuzuweisen, statt sie direkt in der Union zu speichern.Der
NaN
Ansatz speichert alles als Doppelte und verwendet den nicht verwendeten Teil vonNaN
für den zusätzlichen Speicher. Der Vorteil gegenüber der Vereinigungsmethode ist, dass wir das Typfeld speichern. Wenn es ein gültiges Double ist, ist es ein Double, andernfalls ist die Mantisse ein Zeiger auf das tatsächliche Objekt.Denken Sie daran, dies ist jedes Javascript-Objekt. Jede Variable, jeder Wert in einem Objekt, jeder Ausdruck. Wenn wir all das von 96 auf 64 Bit reduzieren können, ist das ziemlich beeindruckend.
Lohnt sich der Hack? Denken Sie daran, dass ein effizientes Javascript sehr gefragt ist. Javascript ist der Engpass in vielen Webanwendungen, weshalb es eine höhere Priorität hat, es schneller zu machen. Aus Performancegründen ist es sinnvoll, ein gewisses Maß an Hackiness einzuführen. In den meisten Fällen wäre es eine schlechte Idee, weil sie einen gewissen Grad an Komplexität mit wenig Gewinn einführt. In diesem speziellen Fall lohnt es sich jedoch, den Speicher und die Geschwindigkeit zu verbessern.
quelle
SmallInteger
.Die Verwendung von NaN für "außergewöhnliche Werte" ist eine bekannte und manchmal hilfreiche Technik, um die Notwendigkeit einer zusätzlichen booleschen Variablen zu vermeiden
this_value_is_invalid
. Mit Bedacht verwendet, kann es einem helfen, seinen Code übersichtlicher, sauberer, einfacher und besser lesbar zu machen, ohne Kompromisse bei der Leistung eingehen zu müssen.Diese Technik hat natürlich einige Tücken (siehe hier http://ppkwok.blogspot.co.uk/2012/11/java-cafe-1-never-write-nan-nan_24.html ), aber in Sprachen wie Java ( oder sehr ähnlich C #) gibt es Standard-Bibliotheksfunktionen
Float.isNaN
, die den Umgang mit NaNs vereinfachen. Natürlich könnten Sie in Java alternativ dieFloat
undDouble
Klasse und in C # die nullbaren Werttypenfloat?
und verwendendouble?
, wodurch Sie die Möglichkeit haben,null
anstelle von NaN ungültige Gleitkommazahlen zu verwenden, aber diese Techniken können die Leistung und den Speicher erheblich negativ beeinflussen Nutzung Ihres Programms.In C ist die Verwendung von NaN zwar nicht zu 100% portabel, aber Sie können es überall dort verwenden, wo der Gleitkommastandard IEEE 754 verfügbar ist. AFAIK dies ist heute fast jede Mainstream-Hardware (oder zumindest die Laufzeitumgebung der meisten Compiler unterstützt dies). Beispielsweise enthält dieser SO-Beitrag einige Informationen, um weitere Einzelheiten zur Verwendung von NaN in C zu erfahren.
quelle