2017-12-20 52 views
2

ペイロード付きのNaNを使用しています(つまり、仮数部に重要な情報が含まれていますが、引き続きNaNとして扱われます)。例えば、そのような値の1つは、符号ビットが1であるFFF8000055550001としてのIEEE-754-1985、NaN /無限大については7FFの指数、(ほとんどのアーキテクチャでは)静かなNaNビットセット、およびペイロードは0x55550001です。Cペイロード付きダブルリテラルNaN

しかし、これにはいくつかの問題があります。ダブルスのため

  • 進リテラルの表記(最良の選択肢、hereを参照してください)だけに思える:まず、これはすべてのリテラルを初期化するために使用することはできません一般的な方法以来、C/C++でのリテラルとして簡単に作成することができませんそれはエイリアシングを回避しますが、関数呼び出し
  • を必要とするサポート 有限値ではなく無限大またはNaNを非NaN値(Iは p1024p-1024を使用して inf Sを得ることができますが、仮数部は、この場合には無視されます)
  • memcpyがいいです
  • reinterpret_castはC++のみです(大丈夫です)が、変数やポインタではなくリテラル
  • 組合型-punningするオペランドが必要ですが、私は

があり、これは一定の静-時間を初期化するために使用することができないと思いますペイロードを持つNaNの静的定数を設定する方法はありますか?システムはIEEE-754-1985に準拠しており、longdoubleは同じエンディアンであると仮定してもよい。

+0

私はあなたがこの目的のために[ユニオン](https://stackoverflow.com/q/2148989/995714)を使用することができますね –

答えて

1
  • memcpyは、関数呼び出しで実装する必要はありません。良いコンパイラがインラインでそれを最適化します。

  • スタティックな記憶期間を持つ定数ユニオンオブジェクトを初期化できます。あなたは、その後のアドレスとキャスト取ることができます化合物リテラル、でこれを行うことができます

+0

これは正しく、余分な変数は最適化される可能性が高いです。しかし、私の具体的な使用のために、他の解決策はわずかに優れています。 – coderforlife

2

:ここ

double d = ((union {unsigned char c[8]; double d; }){ .c={1,0,0,0,0,0,0xf0,0x7f} }).d; 
printf("d=%f\n", d); 

int i; 
printf("d=0x"); 
for (i=0; i<sizeof(double); i++) { 
    unsigned char c = ((unsigned char *)&d)[sizeof(double)-1-i]; 
    printf("%02x", c); 
} 
printf("\n"); 

を、我々はのunsigned charの配列を含む匿名リテラル組合を持っていますサイズ8およびdouble。リテラルの配列フィールドを初期化し、double部分を読み込み、変数を初期化します。

出力:

d=nan 
d=7ff0000000000001 

我々はマクロで少しこれをクリーンアップし、またエンディアンの世話をすることができます。

static_assert(sizeof(double)==8, "unexpected double size"); 

#if __BYTE_ORDER == __BIG_ENDIAN 
# define DOUBLE_LIT(c1,c2,c3,c4,c5,c6,c7,c8) ((union {unsigned char c[8]; double d; }){ .c={c1,c2,c3,c4,c5,c6,c7,c8} }).d 
#elif __BYTE_ORDER == __LITTLE_ENDIAN 
# define DOUBLE_LIT(c1,c2,c3,c4,c5,c6,c7,c8) ((union {unsigned char c[8]; double d; }){ .c={c8,c7,c6,c5,c4,c3,c2,c1} }).d 
#else 
# error unknown endianness 
#fi 

その後、我々はこのようにそれを使用することができます:

double d = DOUBLE_LIT(0x7f,0xf0,0,0,0,0,0,1); 

エンディアンチェックはシステムに依存します。上記はLinux上でどのように実装されているかです。

+0

"テンポラリ配列のアドレスを取っています"と記述されているところではどこでもエラーになります。私は '&'の有無にかかわらず試しました。これはコードがCの代わりにC++としてコンパイルされているためですか?それはhttps://stackoverflow.com/questions/32941846/c-error-taking-address-of-temporary-arrayに従って起こっているようです。 – coderforlife

+0

@ codforlifeほとんどの可能性があります。 CとC++は2つの異なる言語であり、これはそれらが異なる場所の1つです。 Cコードを書いているなら、Cとしてコンパイルしてください。 – dbush

+0

悲しいことに、これは実際にはコンパイル時のオプションがC++として扱われるようにしています。私はそれについて何かできるかどうかを見ていきますが、そうでなければ私のテストコードでうまくいくように見えます。 – coderforlife

1

GCCの(移植性がない)__builtin_nan拡張子は、ペイロードでコンパイル時のNaN定数を生成するために使用できます。そのマニュアルを参照して

この関数は、strtolはで を消費されていたであろうすべてが文字列リテラル与えられた場合、それは コンパイル時の定数と考えていることを十分に早く評価されます。

例:

#include <stdio.h> 

#define MAKE_QNAN_WITH_PAYLOAD(sign, payload) \ 
    sign __builtin_nan(#payload) 

double d = MAKE_QNAN_WITH_PAYLOAD(-, 0x55550001); 

int main(void) 
{ 
    // assume little-endian byte ordering 
    for (int i = sizeof(double)-1; i >= 0; i--) 
    { 
     printf("%.2x", ((unsigned char *)&d)[i]); 
    } 
    putchar('\n'); 

    return 0; 
} 

結果:

fff8000055550001 
+0

これは、静かなビットがどこに行くか(または実際にはシグナルビットであるかなど)、エンディアン、および何もないような、すべての奇妙なことの処理を組み込んだ素晴らしいソリューションです。さらに、この文書を読むと、私はGCC以外のコンパイラのフォールバックとして使用している(コンパイル時ではない)同様に動作するC99関数nan()に導かれました。 – coderforlife

関連する問題