NaNボクシングの目的は何ですか?


44

21世紀Cを読んで、第6章の「例外的な数値をNaNでマークする」セクションに到着しました。ここでは、仮数部のビットを使用して任意のビットパターンを格納し、マーカーまたはポインターとして使用する方法について説明しています(本の言及そのWebKitはこの手法を使用しています)。

このテクニックの有用性を理解したかどうかはよくわかりませんが、ハック(NaNの仮数の値を気にせずハードウェアに依存している)に見えますが、私は慣れていないJavaのバックグラウンドから来ていますCの粗さ

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のエンコーディングを説明し、それが使われていない理由。

この手法の目的は何ですか?スペース/パフォーマンスの利点は、そのハック的な性質のバランスを取るのに十分ですか?


簡単な例を提供できますか?
BЈовић

明確にするために、OPはシグナルNaNを使用できる場所を尋ねています
ラチェットフリーク

1
@ratchetfreak、どう思いますか?
ウィンストンイーバート

@ratchetfreak:WebキットJSValue.hが説明しているように、質問はNaNを通知することではありませんが、新しい何かを発見させてくれてありがとう!
andijcr

1
@Hudson isnan()siはメインの2番目のprintfで使用されます。is_an()の目的は、入力のdoubleのビットパターンがrefグローバル変数内に保存されているものと等しいかどうかをテストすることです。
andijcr

回答:


63

動的に型指定された言語を実装するときは、任意のオブジェクトを保持できる単一の型を持つ必要があります。このために私が知っている3つの異なるアプローチがあります:

まず、ポインターを渡すことができます。これがCPython実装の機能です。すべてのオブジェクトはPyObjectポインターです。これらのポインターは渡され、操作はPyObject構造体の詳細を見て型を判断することで実行されます。

欠点は、数値のような小さな値がボックス化された値として保存されるため、小さな5がメモリのブロックとしてどこかに保存されることです。だから、これはLuaで使用されているユニオンアプローチにつながります。の代わりにPyObject*、各値は、1つのフィールドが型を指定する構造体であり、サポートされているすべての異なる型の和集合です。そうすれば、小さな値にメモリを割り当てることを避け、代わりにそれらを直接共用体に保存します。

このNaNアプローチでは、すべてを2倍として保存し、使用されていない部分をNaN追加のストレージに再利用します。ユニオンメソッドを超える利点は、typeフィールドを保存することです。有効なdoubleの場合、それはdoubleです。それ以外の場合、仮数は実際のオブジェクトへのポインターです。

これはすべてのjavascriptオブジェクトです。すべての変数、オブジェクト内のすべての値、すべての式。これらすべてを96ビットから64ビットに減らすことができれば、非常に印象的です。

ハックする価値はありますか?効率的なJavascriptには多くの需要があることを思い出してください。Javascriptは多くのWebアプリケーションのボトルネックであるため、Javascriptを高速化することが優先事項です。パフォーマンス上の理由から、ある程度のハッキングを導入するのが妥当です。ほとんどの場合、それは少しの利益のためにある程度の複雑さを導入するので、悪い考えでしょう。しかし、この特定のケースでは、メモリと速度を改善する価値があります。


2
実際、CPythonは小さな数字をキャッシュします。参照してくださいhg.python.org/cpython/file/e6cc582cafce/Objects/longobject.c
フィリップクラウド

1
@cpcloud、本当ですが、その詳細は適切とは思えませんでした。
ウィンストンイーバート

1
@WinstonEwertそうですね。私が書いたものを読んだ後、私は同じことを考えました。
フィリップクラウド

2
プリミティブ型のビットを使用してすべての値を「ボックス化」することを避けることは、昔からの手法です。Smalltalkは1970年代にそれを使用し、16ビット整数から1ビットを盗んでオブジェクトポインターまたは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.isNaNNaNを簡単に処理できるような標準ライブラリ関数があります。もちろん、JavaではFloatand Doubleクラスを、C#ではnullable値の型float?double?null代わりに使用できますが、NaNの代わりに無効な浮動小数点数を使用する可能性がありますが、これらの手法はパフォーマンスとメモリに大きな悪影響を与える可能性がありますプログラムの使用。

Cでは、NaNの使用は100%移植可能ではありませんが、それは本当ですが、IEEE 754浮動小数点標準が利用可能なすべての場所で使用できます。私の知る限り、これは今日のほぼすべての主流ハードウェアです(または、少なくともほとんどのコンパイラのランタイム環境でサポートされています)。たとえば、このSO投稿には、CでのNaNの使用に関する詳細を調べるための情報が含まれています。


javaでの自動ボックス化は乱雑であり、避けるべきです。null値を提供できるようにするためにそれを使用するだけではとんでもない、バグが発生しやすい
ラチェットフリーク

WebkitがNaNボクシングを使用する場所にリンクするように質問を編集しました。NaNに'信号に以外、WebKitのがNaNのより広範な用途を持っているようだ
andijcr

2
@ratchetfreak:もちろん、私のポイントをサポート
ドク・ブラウン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.