Mục đích của đấm bốc NaN là gì?


44

Đọc Thế kỷ 21 C Tôi đã đến chương 6 trong phần "Đánh dấu các giá trị số đặc biệt bằng NaNs" , trong đó giải thích việc sử dụng các bit trong lớp phủ để lưu trữ một số mẫu bit tùy ý, để sử dụng chúng làm điểm đánh dấu hoặc con trỏ (cuốn sách đề cập WebKit sử dụng kỹ thuật này).

Tôi không thực sự chắc chắn rằng tôi đã hiểu được tiện ích của kỹ thuật này, tôi thấy đó là một hack (nó phụ thuộc vào phần cứng không quan tâm đến giá trị của mantissa trong NaN) nhưng đến từ nền Java tôi không quen độ nhám của C.

Đây là đoạn mã đặt và đọc điểm đánh dấu trong 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));
}

nó in:

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

và tại webkit của JSValue.h giải thích về mã hóa, nhưng không hiểu tại sao nó được sử dụng.

Mục đích của kỹ thuật này là gì? Là lợi ích của không gian / hiệu suất đủ cao để cân bằng bản chất hackish của nó?


bạn có thể cung cấp một ví dụ đơn giản?
BЈовић

để rõ ràng, OP đang hỏi nơi có thể sử dụng tín hiệu NaN
ratchet freak

1
@ratchetfreak, điều gì khiến bạn nghĩ vậy?
Winston Ewert

@ratchetfreak: câu hỏi không phải là về việc báo hiệu NaN, như bộ webkit JSValue.h giải thích, nhưng cảm ơn bạn đã cho tôi khám phá điều gì đó mới!
andijcr

1
@Hudson isnan () si được sử dụng trong printf thứ hai trong main. Mục đích của is_an () là để kiểm tra xem mẫu bit của double trong đầu vào có bằng với lưu trong biến toàn cục ref hay không.
andijcr

Câu trả lời:


63

Khi bạn đang thực hiện một ngôn ngữ được gõ động, bạn phải có một loại duy nhất có thể chứa bất kỳ đối tượng nào của bạn. Có ba cách tiếp cận khác nhau mà tôi biết về điều này:

Đầu tiên, bạn có thể vượt qua con trỏ. Đây là những gì việc thực hiện CPython làm. Mỗi đối tượng là một PyObjectcon trỏ. Các con trỏ này được truyền xung quanh và các thao tác được thực hiện bằng cách xem chi tiết trong cấu trúc PyObject để tìm ra loại.

Nhược điểm là các giá trị nhỏ như số được lưu trữ dưới dạng giá trị được đóng hộp, vì vậy 5 nhỏ của bạn được lưu trữ dưới dạng một khối bộ nhớ ở đâu đó. Vì vậy, điều này dẫn chúng ta đến cách tiếp cận công đoàn, được sử dụng bởi Lua. Thay vì a PyObject*, mỗi giá trị là một cấu trúc trong đó một trường để chỉ định loại và sau đó kết hợp tất cả các loại được hỗ trợ khác nhau. Bằng cách đó, chúng tôi tránh phân bổ bất kỳ bộ nhớ cho các giá trị nhỏ, thay vào đó lưu trữ chúng trực tiếp trong liên minh.

Cách NaNtiếp cận lưu trữ mọi thứ như nhân đôi và tái sử dụng phần không sử dụng NaNcho bộ nhớ bổ sung. Ưu điểm so với phương thức union là chúng ta lưu trường loại. Nếu đó là một đôi hợp lệ, thì đó là gấp đôi nếu không thì mantissa là một con trỏ đến đối tượng thực tế.

Hãy nhớ rằng, đây là mọi đối tượng javascript. Mỗi biến, mọi giá trị trong một đối tượng, mọi biểu thức. Nếu chúng ta có thể giảm tất cả những thứ đó từ 96 bit xuống còn 64 bit thì khá ấn tượng.

Có đáng để hack không? Hãy nhớ lại rằng có rất nhiều nhu cầu về Javascript hiệu quả. Javascript là nút cổ chai trong nhiều ứng dụng web và vì vậy làm cho nó nhanh hơn là ưu tiên cao hơn. Thật hợp lý khi giới thiệu một mức độ hack nhất định vì lý do hiệu suất. Đối với hầu hết các trường hợp, đó là một ý tưởng tồi, bởi vì nó giới thiệu một mức độ phức tạp để đạt được ít lợi ích. Nhưng trong trường hợp cụ thể này, nó đáng để cải thiện bộ nhớ và tốc độ.


2
Trên thực tế CPython lưu trữ số lượng nhỏ. Xem hg.python.org/cpython/file/e6cc582cafce/Objects/longobject.c
Đám mây Phillip

1
@cpcloud, đúng, nhưng chi tiết đó dường như không thích hợp.
Winston Ewert

1
@WinstonEwert Bạn nói đúng. Tôi nghĩ điều tương tự sau khi tôi đọc những gì tôi đã viết.
Đám mây Phillip

2
Sử dụng các bit thuộc loại nguyên thủy để tránh "quyền anh" tất cả các giá trị là một kỹ thuật được tôn vinh theo thời gian. Smalltalk đã sử dụng nó trong những năm 1970, đánh cắp một bit từ số nguyên 16 bit để báo hiệu một con trỏ đối tượng hoặc 15 bit SmallInteger.
Jonathan Eunice

2
@JonathanEunice, thật sao? Điều đó chỉ làm tôi ngạc nhiên vì thực sự không có phạm vi dài trong 16 bit mà tôi sẵn sàng từ bỏ một chút.
Winston Ewert

7

Sử dụng NaN cho "các giá trị đặc biệt" là một kỹ thuật nổi tiếng và đôi khi hữu ích để tránh sự cần thiết của một biến boolean bổ sung this_value_is_invalid. Được sử dụng một cách khôn ngoan, nó có thể giúp người ta làm cho mã của mình ngắn gọn hơn, gọn gàng hơn, đơn giản hơn, dễ đọc hơn mà không có sự đánh đổi hiệu năng.

Kỹ thuật này có một số cạm bẫy, tất nhiên (xem tại đây http://ppkwok.blogspot.co.uk/2012/11/java-cafe-1-never-write-nan-nan_24.html ), nhưng bằng các ngôn ngữ như Java ( hoặc C # rất giống nhau) có các hàm thư viện tiêu chuẩn muốn Float.isNaNlàm cho việc xử lý NaN trở nên đơn giản. Tất nhiên, trong Java, bạn có thể sử dụng xen kẽ FloatDoublelớp và trong C # các loại giá trị không thể float?double?cung cấp cho bạn khả năng sử dụng nullthay vì NaN cho các số dấu phẩy động không hợp lệ, nhưng các kỹ thuật này có thể có ảnh hưởng tiêu cực đáng kể đến hiệu suất và bộ nhớ sử dụng chương trình của bạn.

Trong C, việc sử dụng NaN không phải là 100% di động, điều đó là đúng, nhưng bạn có thể sử dụng nó ở mọi nơi có tiêu chuẩn điểm nổi IEEE 754. AFAIK điều này gần như là mọi phần cứng chính hiện nay (hoặc ít nhất là môi trường thời gian chạy của hầu hết các trình biên dịch đều hỗ trợ nó). Ví dụ: bài đăng SO này chứa một số thông tin để tìm hiểu thêm chi tiết về việc sử dụng NaN trong C.


tính năng tự động đấm bốc trong java rất lộn xộn và nên tránh, chỉ cần sử dụng nó để có thể cung cấp giá trị null là vô lý và dễ bị lỗi
ratchet freak

tôi đã chỉnh sửa câu hỏi để liên kết đến nơi webkit sử dụng NaN-boxing. Có vẻ như webkit có cách sử dụng NaN rộng hơn, ngoài việc báo hiệu 'NaN'
andijcr

2
@ratchetfreak: tất nhiên hỗ trợ quan điểm của tôi
Doc Brown
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.