Qual è lo scopo del pugilato NaN?


44

Leggendo il 21 ° secolo C sono arrivato al capitolo 6 nella sezione "Contrassegnare valori numerici eccezionali con NaNs" , dove spiega l'uso dei bit nella mantissa per memorizzare alcuni schemi di bit arbitrari, per usarli come marcatori o puntatori (il libro menziona che WebKit utilizza questa tecnica).

Non sono davvero sicuro di aver compreso l'utilità di questa tecnica, che vedo come un hack (si basa sull'hardware che non si preoccupa del valore della mantissa in una NaN) ma che proviene da un background Java a cui non sono abituato la rugosità di C.

Ecco lo snippet di codice che imposta e legge un marker in un 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));
}

stampa:

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

e su JSValue.h webkit spiega la codifica, ma non il motivo per cui viene utilizzata.

Qual è lo scopo di questa tecnica? I vantaggi di spazio / prestazioni sono abbastanza alti da bilanciare la sua natura hacker?


puoi fornire un semplice esempio?
BЈовић

per essere chiari, l'OP chiede dove si possano usare i NaN di segnalazione
maniaco del cricchetto

1
@ratchetfreak, cosa te lo fa pensare?
Winston Ewert,

@ratchetfreak: la domanda non riguarda la segnalazione di NaN, come spiega il webkit JSValue.h, ma grazie per avermi permesso di scoprire qualcosa di nuovo!
andijcr

1
@Hudson isnan () si è usato nella seconda stampa principale. Lo scopo di is_an () è di verificare se il modello di bit del doppio in input è uguale a quello salvato all'interno della variabile globale ref.
andijcr

Risposte:


63

Quando stai implementando un linguaggio tipizzato in modo dinamico, devi avere un solo tipo che può contenere uno qualsiasi dei tuoi oggetti. Ci sono tre diversi approcci di cui sono consapevole per questo:

Innanzitutto, puoi passare i puntatori. Questo è ciò che fa l'implementazione di CPython. Ogni oggetto è un PyObjectpuntatore. Questi puntatori vengono passati e le operazioni vengono eseguite osservando i dettagli nella struttura PyObject per capire il tipo.

Lo svantaggio è che piccoli valori come i numeri vengono memorizzati come valori inscatolati, quindi il tuo piccolo 5 viene archiviato come un blocco di memoria da qualche parte. Quindi questo ci porta all'approccio sindacale, che viene utilizzato da Lua. Invece di a PyObject*, ogni valore è una struttura in cui un campo specifica il tipo e quindi un'unione di tutti i diversi tipi supportati. In questo modo evitiamo di allocare memoria per piccoli valori, invece di archiviarli direttamente nell'unione.

L' NaNapproccio memorizza tutto come doppio e riutilizza la parte inutilizzata NaNper l'archiviazione aggiuntiva. Il vantaggio rispetto al metodo union è che salviamo il campo type. Se è un doppio valido, è un doppio, altrimenti la mantissa è un puntatore all'oggetto reale.

Ricorda, questo è ogni oggetto JavaScript. Ogni variabile, ogni valore in un oggetto, ogni espressione. Se siamo in grado di ridurre tutti quelli da 96 bit a 64 bit, questo è piuttosto impressionante.

Vale la pena l'hack? Ricordiamo che c'è molta richiesta di Javascript efficiente. Javascript è il collo di bottiglia in molte applicazioni web, quindi renderlo più veloce è una priorità più alta. È ragionevole introdurre un certo grado di pirateria informatica per motivi di prestazioni. Nella maggior parte dei casi, sarebbe una cattiva idea, perché introduce un certo grado di complessità per un piccolo guadagno. Ma in questo caso specifico, vale la pena migliorare la memoria e la velocità.


2
In realtà CPython memorizza nella cache piccoli numeri. Vedi hg.python.org/cpython/file/e6cc582cafce/Objects/longobject.c
Phillip Cloud

1
@cpcloud, vero, ma quel dettaglio non sembrava pertinente.
Winston Ewert,

1
@WinstonEwert Hai ragione. Ho pensato la stessa cosa dopo aver letto quello che avevo scritto.
Phillip Cloud,

3
L'uso di bit di tipo primitivo per evitare il "pugilato" di tutti i valori è una tecnica consolidata. Smalltalk lo usò negli anni '70, rubando un bit da numeri interi a 16 bit per segnalare un puntatore oggetto o 15 bit SmallInteger.
Jonathan Eunice,

2
@JonathanEunice, davvero? Questo mi sorprende solo perché non c'è davvero un lungo raggio in 16 bit che sarei disposto a rinunciare un po '.
Winston Ewert,

7

L'uso di NaN per "valori eccezionali" è una tecnica ben nota e talvolta utile per evitare la necessità di una variabile booleana aggiuntiva this_value_is_invalid. Utilizzato con saggezza, può aiutare a rendere il suo codice più conciso, più pulito, più semplice, meglio leggibile senza compromessi sulle prestazioni.

Questa tecnica ha alcune insidie, ovviamente (vedi qui http://ppkwok.blogspot.co.uk/2012/11/java-cafe-1-never-write-nan-nan_24.html ), ma in lingue come Java ( o C # molto simile) ci sono funzioni di libreria standard come Float.isNaNsemplificare la gestione dei NaN. Naturalmente, in Java è possibile utilizzare in alternativa la Floate Doubledi classe e in C # il nullable tipi di valore float?e double?, dandovi la possibilità di utilizzare nullal posto di NaN per i numeri in virgola mobile non validi, ma quelle tecniche possono avere un significativo impatto negativo sulle prestazioni e la memoria utilizzo del tuo programma.

In C l'uso di NaN non è portatile al 100%, questo è vero, ma è possibile utilizzarlo ovunque sia disponibile lo standard IEEE 754 in virgola mobile. AFAIK, questo è quasi tutti gli hardware tradizionali oggi (o almeno l'ambiente di runtime della maggior parte dei compilatori lo supporta). Ad esempio, questo post SO contiene alcune informazioni per scoprire maggiori dettagli sull'uso di NaN in C.


l'auto-boxing in java è disordinato e dovrebbe essere evitato, solo usarlo per essere in grado di fornire un valore null è ridicolo e soggetto a bug
maniaco del cricchetto

ho modificato la domanda per collegarmi a dove webkit utilizza NaN-boxing. Sembra che il webkit abbia un uso più ampio di NaN, oltre a segnalare "NaN"
andijcr

2
@ratchetfreak: questo supporta il mio punto, ovviamente
Doc Brown,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.