Apa tujuan dari tinju NaN?

44

Membaca Abad 21 C Saya tiba di bab 6 di bagian "Menandai Nilai Numerik Luar Biasa dengan NaNs" , di mana ia menjelaskan penggunaan bit dalam mantissa untuk menyimpan beberapa pola bit sewenang-wenang, untuk menggunakannya sebagai marker atau pointer (buku ini menyebutkan bahwa WebKit menggunakan teknik ini).

Saya tidak begitu yakin saya mengerti kegunaan teknik ini, yang saya lihat sebagai peretasan (ini bergantung pada perangkat keras yang tidak peduli pada nilai mantissa dalam NaN) tetapi berasal dari latar belakang Java yang tidak biasa saya gunakan. kekasaran C.

Berikut ini potongan kode yang menetapkan dan membaca penanda di 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));
}

itu mencetak:

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

dan di webkit JSValue.h menjelaskan pengkodean, tetapi tidak mengapa itu digunakan.

Apa tujuan dari teknik ini? Apakah manfaat ruang / kinerja cukup tinggi untuk menyeimbangkan sifat peretasannya?

andijcr
sumber
dapatkah Anda memberikan contoh sederhana?
BЈовић
untuk menjadi jelas OP bertanya di mana pensinyalan NaN dapat digunakan
ratchet freak
1
@ scratchetfreak, apa yang membuatmu berpikir begitu?
Winston Ewert
@ scratchetfreak: pertanyaannya bukan tentang pensinyalan NaN, seperti yang dijelaskan oleh webkit JSValue.h, Tapi terima kasih telah mengizinkan saya menemukan sesuatu yang baru!
andijcr
1
@Hudson isnan () si digunakan pada printf kedua di main. Tujuan is_an () adalah untuk menguji apakah pola bit input ganda sama dengan yang disimpan di dalam variabel global ref.
andijcr

Jawaban:

63

Saat Anda menerapkan bahasa yang diketik secara dinamis, Anda harus memiliki satu jenis yang dapat menampung objek apa pun. Ada tiga pendekatan berbeda yang saya ketahui untuk ini:

Pertama, Anda bisa membagikan petunjuk. Inilah yang dilakukan oleh implementasi CPython. Setiap objek adalah PyObjectpointer. Pointer ini dapat diedarkan dan operasi dilakukan dengan melihat detail pada struct PyObject untuk mengetahui tipe.

Kerugiannya adalah bahwa nilai-nilai kecil seperti angka disimpan sebagai nilai kotak, Jadi 5 anak Anda disimpan sebagai blok memori di suatu tempat. Jadi ini membawa kita ke pendekatan persatuan, yang digunakan oleh Lua. Alih-alih PyObject*, setiap nilai adalah struct yang satu bidang untuk menentukan jenis, dan kemudian penyatuan semua jenis yang didukung berbeda. Dengan cara itu kami menghindari mengalokasikan memori apa pun untuk nilai-nilai kecil, alih-alih menyimpannya langsung di serikat.

The NaNPendekatan toko segala sesuatu sebagai ganda, dan menggunakan kembali bagian yang tidak terpakai dari NaNuntuk penyimpanan ekstra. Keuntungan dibandingkan metode penyatuan adalah kita menyimpan bidang tipe. Jika itu adalah dobel yang valid, itu adalah dobel jika tidak maka mantissa adalah sebuah pointer ke objek yang sebenarnya.

Ingat, ini adalah setiap objek javascript. Setiap variabel, setiap nilai dalam suatu objek, setiap ekspresi. Jika kita bisa mengurangi semuanya dari 96 bit menjadi 64 bit, itu cukup mengesankan.

Apakah ini layak untuk diretas? Ingatlah bahwa ada banyak permintaan untuk Javascript yang efisien. Javascript adalah penghambat dalam banyak aplikasi web, dan membuatnya lebih cepat adalah prioritas yang lebih tinggi. Masuk akal untuk memperkenalkan tingkat peretasan tertentu untuk alasan kinerja. Untuk sebagian besar kasus, itu adalah ide yang buruk, karena memperkenalkan tingkat kerumitan untuk sedikit keuntungan. Namun dalam kasus khusus ini, bermanfaat untuk peningkatan memori dan kecepatan.

Winston Ewert
sumber
2
Sebenarnya cache CPython angka kecil. Lihat hg.python.org/cpython/file/e6cc582cafce/Objects/longobject.c
Phillip Cloud
1
@ cpcloud, benar, tapi detail itu sepertinya tidak relevan.
Winston Ewert
1
@ WinstonEwert Anda benar. Saya memikirkan hal yang sama setelah saya membaca apa yang saya tulis.
Phillip Cloud
2
Menggunakan bit dari tipe primitif untuk menghindari "tinju" semua nilai adalah teknik yang dihormati waktu. Smalltalk menggunakannya pada tahun 1970-an, mencuri satu bit dari bilangan bulat 16-bit untuk memberi sinyal objek pointer atau 15-bit SmallInteger.
Jonathan Eunice
2
@ Jonathanunice, benarkah? Itu hanya mengejutkan saya karena benar-benar tidak ada rentang dalam 16 bit yang saya bersedia untuk menyerah sedikit.
Winston Ewert
7

Menggunakan NaN untuk "nilai luar biasa" adalah teknik yang terkenal dan terkadang membantu untuk menghindari kebutuhan variabel boolean tambahan this_value_is_invalid. Digunakan dengan bijak, ini dapat membantu seseorang membuat kodenya lebih ringkas, lebih bersih, lebih sederhana, lebih mudah dibaca tanpa ada kompromi kinerja.

Teknik ini memiliki beberapa jebakan, tentu saja (lihat di sini http://ppkwok.blogspot.co.uk/2012/11/java-cafe-1-never-write-nan-nan_24.html ), tetapi dalam bahasa seperti Jawa ( atau C # sangat mirip) ada fungsi perpustakaan standar ingin Float.isNaNmembuat berurusan dengan NaNs sederhana. Tentu saja, di Jawa Anda bisa menggunakan alternatif Floatdan Doublekelas dan di C # tipe nilai yang dapat dibatalkan float?dan double?, memberi Anda kemungkinan menggunakan nullalih-alih NaN untuk angka floating point yang tidak valid, tetapi teknik-teknik tersebut dapat memiliki pengaruh negatif yang signifikan terhadap kinerja dan memori penggunaan program Anda.

Dalam C penggunaan NaN tidak 100% portabel, itu benar, tetapi Anda dapat menggunakannya di mana-mana di mana standar titik mengambang IEEE 754 tersedia. AFAIK ini hampir setiap perangkat keras utama saat ini (atau setidaknya lingkungan runtime dari sebagian besar kompiler mendukungnya). Misalnya, pos SO ini berisi beberapa informasi untuk mengetahui detail lebih lanjut tentang penggunaan NaN dalam C.

Doc Brown
sumber
auto-tinju di java berantakan dan harus dihindari, hanya menggunakannya untuk dapat memberikan nilai nol adalah konyol dan rentan terhadap bug
ratchet freak
saya mengedit pertanyaan untuk menautkan ke tempat webkit menggunakan tinju NaN. Tampaknya webkit memiliki penggunaan NaN yang lebih luas, selain untuk memberi sinyal 'NaN'
andijcr
2
@ scratchetfreak: yang mendukung pendapat saya, tentu saja
Doc Brown