Какова цель NaN бокса?

44

Чтение 21-го века C Я пришел к главе 6 в разделе «Маркировка исключительных числовых значений с помощью NaN» , где объясняется использование битов в мантиссе для хранения некоторых произвольных битовых комбинаций, для их использования в качестве маркеров или указателей (упоминается в книге что WebKit использует эту технику).

Я не совсем уверен, что я понял полезность этой техники, которую я вижу как хак (она опирается на аппаратное обеспечение, не заботящееся о значении мантиссы в NaN), но исходя из фона Java, к которому я не привык шероховатость кл.

Вот фрагмент кода, который устанавливает и читает маркер в 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));
}

это печатает:

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

и в JSValue.h webkit объясняет кодировку, но не почему она используется.

Какова цель этой техники? Достаточно ли высоки преимущества пространства / производительности, чтобы сбалансировать его хакерский характер?

andijcr
источник
Можете ли вы привести простой пример?
BЈовић
чтобы очистить OP спрашивает , где сигнальные NaNs могут быть использованы
трещотки урод
1
@ratchetfreak, что заставляет тебя так думать?
Уинстон Эверт
@ratchetfreak: вопрос не в том, чтобы сигнализировать NaN, как объясняет веб-набор JSValue.h, но спасибо, что позволили мне открыть что-то новое!
andijcr
1
@Hudson isnan () si используется во втором printf в основном. Цель is_an () - проверить, совпадает ли битовая комбинация двойного входа, сохраненная внутри глобальной переменной ref.
andijcr

Ответы:

63

Когда вы реализуете динамически типизированный язык, у вас должен быть один тип, который может содержать любой из ваших объектов. Для этого есть три разных подхода:

Во-первых, вы можете обойти указатели. Это то, что делает реализация CPython. Каждый объект является PyObjectуказателем. Эти указатели передаются и операции выполняются путем просмотра деталей в структуре PyObject, чтобы выяснить тип.

Недостатком является то, что маленькие значения, такие как числа, сохраняются как коробочные значения, поэтому ваши маленькие 5 хранятся где-то как блок памяти. Так что это приводит нас к объединенному подходу, который используется Lua. Вместо a PyObject*каждое значение представляет собой структуру, в которой одно поле должно указывать тип, а затем объединение всех различных поддерживаемых типов. Таким образом мы избегаем выделения памяти для небольших значений, вместо этого сохраняя их непосредственно в объединении.

NaNПодход хранит все , как удваивается, и повторно неиспользованную часть NaNдля дополнительного хранения. Преимущество перед методом объединения состоит в том, что мы сохраняем поле типа. Если это правильный double, это double, в противном случае мантисса является указателем на реальный объект.

Помните, это каждый объект javascript. Каждая переменная, каждое значение в объекте, каждое выражение. Если мы сможем сократить все это с 96 до 64 бит, это будет довольно впечатляюще.

Это стоит взломать? Напомним, что существует большой спрос на эффективный Javascript. Javascript является узким местом во многих веб-приложениях, поэтому его ускорение является более высоким приоритетом. Разумно ввести определенную степень хакерства по соображениям производительности. В большинстве случаев это была бы плохая идея, потому что она вводит некоторую сложность за небольшую выгоду. Но в данном конкретном случае это стоит для улучшения памяти и скорости.

Уинстон Эверт
источник
2
На самом деле CPython кэширует небольшие числа. См. Hg.python.org/cpython/file/e6cc582cafce/Objects/longobject.c
Phillip Cloud
1
@cpcloud, правда, но эта деталь не казалась уместной.
Уинстон Эверт
1
@WinstonEwert Ты прав. Я думал то же самое после того, как прочитал то, что написал.
Филлип Облако
3
Использование битов примитивного типа, чтобы избежать «упаковки» всех значений, является проверенной временем техникой. Smalltalk использовал его в 1970-х годах, крадя один бит из 16-битных целых чисел, чтобы сигнализировать либо указатель объекта, либо 15-битный SmallInteger.
Джонатан Юнис
2
@JonathanEunice, правда? Это просто удивляет меня, потому что в 16 битах нет большого диапазона, от которого я бы хотел немного отказаться.
Уинстон Эверт
7

Использование NaN для «исключительных значений» - это хорошо известный и иногда полезный метод, позволяющий избежать необходимости в дополнительной булевой переменной this_value_is_invalid. При правильном использовании это может помочь сделать его код более лаконичным, понятным, простым, лучше читаемым без каких-либо компромиссов в производительности.

Конечно, у этой техники есть некоторые подводные камни (см. Здесь http://ppkwok.blogspot.co.uk/2012/11/java-cafe-1-never-write-nan-nan_24.html ), но в таких языках, как Java ( или очень похожий C #) существуют стандартные библиотечные функции, такие как Float.isNaNупрощение работы с NaN. Конечно, в Java можно использовать в качестве альтернативы, Floatи Doubleкласс , и в C # Если обнуляемые типов значений float?и double?, давая вам возможность использовать nullвместо NaN для недействительных чисел с плавающей точкой, но эти методы могут оказать существенное негативное влияние на производительности и памяти использование вашей программы.

В Си использование NaN не является переносимым на 100%, это правда, но вы можете использовать его везде, где доступен стандарт IEEE 754 с плавающей запятой. Насколько нам известно, это практически все основные аппаратные средства сегодня (или, по крайней мере, среда поддержки большинства компиляторов его поддерживает). Например, этот пост SO содержит некоторую информацию, чтобы узнать больше об использовании NaN в C.

Док Браун
источник
автобокс в java грязный и его следует избегать, просто использование его для предоставления нулевого значения смешно и подвержено ошибкам
ratchet freak
я отредактировал вопрос, чтобы указать, где webkit использует NaN-бокс. Похоже, что в вебките более широко используется NaN, кроме как для сигнализации «NaN»
andijcr
2
@ratchetfreak: это поддерживает мою точку зрения, конечно
Док Браун