web-dev-qa-db-ja.com

doubleの配列でmemset(、0、)を使用することは合法ですか?

Doubleの配列をゼロにすること(memset(、0、)を使用)またはdoubleを含む構造体を使用することは合法ですか?

この質問は、2つの異なることを意味します。

(1)C規格の観点から、このUBはそうではありませんか? (固定プラットフォームでは、このUBはどのようにできますか...それはすべてフローティング表現に依存します...)

(2)実用的な観点から:Intelプラットフォームで大丈夫ですか? (標準が何を言っていても)。

37
Andrei

C99標準のAnnexFは次のように述べています。

この付録では、IEC60559浮動小数点標準のC言語サポートを指定しています。IEC 60559浮動小数点標準は、マイクロプロセッサシステム用のバイナリ浮動小数点演算、第2版(IEC 60559:1989)、以前に指定されたIEC 559:1989およびバイナリ浮動小数点演算のIEEE標準(ANSI/IEEE 754-1985)基数に依存しない浮動小数点演算のIEEE標準(ANSI/IEEE 854-1987)バイナリ標準を一般化して、基数とワード長への依存関係を削除します。IEC 60559は、一般に、 IEC 60559操作、IEC 60559形式など)のような浮動小数点標準。__STDC_IEC_559__を定義する実装は、の仕様に準拠する必要があります。この付録。C言語とIEC 60559)の間のバインディングが示されている場合、特に明記されていない限り、IEC 60559で指定された動作が参照により採用されます。

そして、その直後、

Cフローティングタイプは、次のようにIEC 60559形式に一致します。

  • floatタイプは、IEC 60559単一フォーマットに一致します。
  • double型は、IEC 60559double形式に一致します。

したがって、IEC 60559は基本的にIEEE754-1985であり、これは8つのゼロバイトが0.0を意味することを指定している場合(@David Heffernanが言ったように)、これは__STDC_IEC_559__が定義されていることを意味します。 memsetで0.0の初期化を安全に行うことができます。

30
Matteo Italia

IEEE754について話している場合、標準では+0.0から倍精度を8つのゼロバイトとして定義しています。 IEEE754浮動小数点に支えられていることがわかっている場合、これは明確に定義されています。

Intelに関しては、Intel x86/x64でIEEE754を使用しないコンパイラは考えられません。

14
David Heffernan

David Heffernanは、あなたの質問のパート(2)に良い答えを与えました。パート(1)の場合:

C99 標準は、一般的な場合の浮動小数点値の表現について保証しません。 §6.2.6.1は言う:

この節に記載されている場合を除き、すべてのタイプの表現は指定されていません。

...そしてその節は浮動小数点についてこれ以上言及していません。

あなたが言った:

(固定プラットフォームでは、このUBはどのようにできますか...それはすべてフローティング表現に依存します...)

確かに-"undefinedの動作"、 "unspecifiedの動作には違いがあります"および"implementation-definedの動作 ":

  • undefinedの動作」は、何かが発生する可能性があることを意味します(ランタイムクラッシュを含む)。
  • unspecificedbehaviour」は、コンパイラーが自由に適切な方法で何かを実装できることを意味しますが、実装の選択を文書化する必要はありません。 ;
  • implementation-definedbehaviour」は、コンパイラーが自由に適切な方法で何かを実装できることを意味し、その選択を文書化することになっています(たとえば、 [〜#〜] gcc [〜#〜] );の最新リリースで文書化されている実装の選択については、 ここ を参照してください。

したがって、浮動小数点表現はunspecifiedの動作であるため、プラットフォームごとに文書化されていない方法で変化する可能性があります(ここでの「プラットフォーム」は「組み合わせ」を意味します)単なる「ハードウェア」ではなく、「ハードウェアとコンパイラの」)。

(Matteo Italiaの回答で説明されているように、+0.0が定義されている場合、all-bits-zeroが__STDC_IEC_559__になるようにdoubleが表されるという保証が実際にどれほど役立つかはわかりません。たとえば、- GCCはこれを定義しません はIEEE 754/IEC 60559を多くのハードウェアプラットフォームで使用していますが)

5

これに問題があるマシンに遭遇する可能性は低いですが、質問のタイトルで示しているようにarraysについて実際に話している場合、およびこれらの配列がコンパイル時の既知の長さ(つまり、not VLA)の場合、それらを初期化するだけで、おそらくさらに便利になります。

double A[133] = { 0 };

常に動作するはずです。後でそのような配列を再度ゼロにする必要があり、コンパイラが最新のC(C99)に準拠している場合は、複合リテラルを使用してこれを行うことができます。

memcpy(A, (double const[133]){ 0 }, 133*sizeof(double));

最新のコンパイラでは、これはmemsetと同じくらい効率的ですが、doubleの特定のエンコーディングに依存しないという利点があります。

4
Jens Gustedt

Matteo Italiaが言うように、それは標準によれば合法ですが、私はそれを使用しません。何かのようなもの

double *p = V, *last = V + N; // N - count
while(p != last) *(p++) = 0;

少なくとも2倍高速です。

2
Andrei Sfrent

まあ、ゼロ化は「合法」だと思います(結局のところ、通常のバッファーをゼロ化することです)が、標準で結果の論理値について何かを想定できるかどうかはわかりません。私の推測では、C標準はそれを未定義のままにしていると思います。

0
user541686

Memsetを使用することは「合法」です。問題は、array [x] == 0.0が真であるビットパターンを生成するかどうかです。基本的なC標準ではそれが真実である必要はありませんが、そうでない例を聞いてみたいと思います。

Memsetは、IBM-AIX、HP-UX(PARISC)、HP-UX(IA-64)、Linux(IA-64、私は思う)では0.0に相当するようです。

    {
    double dFloat1 = 0.0;
    double dFloat2 = 111111.1111111;

    memset(&dFloat2, 0, sizeof(dFloat2));

    if(dFloat1 == dFloat2)
    {
        fprintf(stdout, "memset appears to be equivalent to = 0.0\n");
    }
    else
    {
        fprintf(stdout, "memset is NOT equivalent to = 0.0\n");
    }
}
0
davep