Czytając 21 wiek C Doszedłem do rozdziału 6 w sekcji „Oznaczanie wyjątkowych wartości liczbowych za pomocą NaNs” , gdzie wyjaśnia użycie bitów w mantysie do przechowywania niektórych dowolnych wzorów bitów, wykorzystywania ich jako znaczników lub wskaźników (książka wspomina że WebKit korzysta z tej techniki).
Nie jestem do końca pewien, czy zrozumiałem użyteczność tej techniki, którą widzę jako hack (polega ona na sprzęcie nie dbającym o wartość mantysy w NaN), ale pochodzącym ze środowiska Java, do którego nie jestem przyzwyczajony szorstkość C.
Oto fragment kodu, który ustawia i odczytuje znacznik w NaN
#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));
}
drukuje:
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
a na stronie JSValue.h webkit wyjaśnia kodowanie, ale nie wyjaśnia, dlaczego jest używane.
Jaki jest cel tej techniki? Czy zalety przestrzeni / wydajności są wystarczająco wysokie, aby zrównoważyć jej hackerski charakter?
źródło
Odpowiedzi:
Kiedy wdrażasz dynamicznie pisany język, musisz mieć jeden typ, który może pomieścić dowolny z twoich obiektów. Są trzy różne podejścia, o których wiem w tym zakresie:
Po pierwsze, możesz omijać wskaźniki. Tak właśnie działa implementacja CPython. Każdy obiekt jest
PyObject
wskaźnikiem. Wskaźniki te są omijane, a operacje są wykonywane przez sprawdzenie szczegółów w strukturze PyObject w celu ustalenia typu.Wadą jest to, że małe wartości, takie jak liczby, są przechowywane jako wartości pudełkowe, więc twoja mała 5 jest przechowywana gdzieś jako blok pamięci. To prowadzi nas do podejścia związkowego, z którego korzysta Lua. Zamiast a
PyObject*
każda wartość jest strukturą, której jedno pole określa typ, a następnie połączenie wszystkich obsługiwanych typów. W ten sposób unikamy przydzielania pamięci na małe wartości, zamiast przechowywania ich bezpośrednio w unii.NaN
Wszystko przechowuje podejście jak deblu i ponownie wykorzystuje niewykorzystaną częśćNaN
dla dodatkowej pamięci masowej. Zaletą metody łączenia jest to, że zapisujemy pole typu. Jeśli jest to poprawna podwójna, to podwójna, w przeciwnym razie mantysa jest wskaźnikiem do rzeczywistego obiektu.Pamiętaj, to jest każdy obiekt javascript. Każda zmienna, każda wartość w obiekcie, każde wyrażenie. Jeśli uda nam się zredukować wszystkie z 96 do 64 bitów, robi to wrażenie.
Czy warto hack? Przypomnij sobie, że istnieje duże zapotrzebowanie na wydajny Javascript. JavaScript jest wąskim gardłem w wielu aplikacjach internetowych, dlatego przyspieszenie jest priorytetem. Rozsądne jest wprowadzenie pewnego stopnia włamań ze względu na wydajność. W większości przypadków byłby to zły pomysł, ponieważ wprowadza pewien stopień złożoności przy niewielkim zysku. Ale w tym konkretnym przypadku warto poprawić pamięć i prędkość.
źródło
SmallInteger
.Używanie NaN do „wyjątkowych wartości” jest dobrze znaną i czasem pomocną techniką pozwalającą uniknąć potrzeby dodatkowej zmiennej boolowskiej
this_value_is_invalid
. Używana mądrze, może pomóc uczynić jego kod bardziej zwięzłym, czystszym, prostszym, lepiej czytelnym bez żadnych kompromisów wydajnościowych.Ta technika ma oczywiście pewne pułapki (patrz tutaj http://ppkwok.blogspot.co.uk/2012/11/java-cafe-1-never-write-nan-nan_24.html ), ale w językach takich jak Java ( lub bardzo podobny C #) istnieją standardowe funkcje biblioteczne, takie jak
Float.isNaN
uproszczenie obsługi NaN. Oczywiście w Javie można użyć alternatywnieFloat
iDouble
klasy i C # pustych typy wartościfloat?
idouble?
, co daje możliwość zastosowanianull
zamiast NaN dla niepoprawnych liczb zmiennoprzecinkowych, ale te techniki mogą mieć znaczący wpływ negatywny na wydajność i pamięć korzystanie z twojego programu.W C użycie NaN nie jest w 100% przenośne, to prawda, ale można go używać wszędzie tam, gdzie dostępny jest standard zmiennoprzecinkowy IEEE 754. AFAIK jest to prawie każdy obecnie główny sprzęt (lub przynajmniej środowisko uruchomieniowe większości kompilatorów go obsługuje). Na przykład ten post SO zawiera pewne informacje, aby dowiedzieć się więcej szczegółów na temat używania NaN w C.
źródło