TLDR; 次のコードは、未定義 (または未指定) の動作を呼び出しますか?
#include <stdio.h>
#include <string.h>
void printme(void *c, size_t n)
{
/* print n bytes in binary */
}
int main() {
long double value1 = 0;
long double value2 = 0;
memset( (void*) &value1, 0x00, sizeof(long double));
memset( (void*) &value2, 0x00, sizeof(long double));
/* printf("value1: "); */
/* printme(&value1, sizeof(long double)); */
/* printf("value2: "); */
/* printme(&value2, sizeof(long double)); */
value1 = 0.0;
value2 = 1.0;
printf("value1: %Lf\n", value1);
printme(&value1, sizeof(long double));
printf("value2: %Lf\n", value2);
printme(&value2, sizeof(long double));
return 0;
}
私の x86-64 マシンでは、出力はコンパイラに渡された特定の最適化フラグ (gcc-4.8.0、-O0 と -O1) によって異なります。
-O0 を使用すると、
value1: 0.000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
value2: 1.000000
00000000 00000000 00000000 00000000 00000000 00000000 00111111 11111111
10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
-O1を使用している間、私は得る
value1: 0.000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
value2: 1.000000
00000000 00000000 00000000 00000000 00000000 01000000 00111111 11111111
10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
最後から 2 行目に余分な 1 があることに注意してください。また、memset の後に印刷命令のコメントを外すと、その 1 が消えます。これは、次の 2 つの事実に依存しているようです。
- long double はパディングされます。つまり、sizeof(long double) = 16 ですが、10 バイトしか使用されません。
- memset への呼び出しが最適化される可能性があります
- long double のパディング ビットは予告なしに変更される可能性があります。つまり、value1 と value2 の浮動小数点演算は、パディング ビットをスクランブルするように見えます。
私はコンパイルして-std=c99 -Wall -Wextra -Wpedantic
いて警告が出ないので、これが厳密なエイリアシング違反のケースであるかどうかはわかりません (しかし、そうかもしれません)。通過-fno-strict-aliasing
しても何も変わりません。
コンテキストは、ここで説明されている HDF5 ライブラリで見つかったバグです。HDF5 は、浮動小数点型のネイティブ ビット表現を把握するために多少の調整を行いますが、パディング ビットがゼロのままでないと混乱します。
そう:
- これは未定義の動作ですか?
- これは厳密なエイリアシング違反ですか?
ありがとう。
編集: これは printme のコードです。あまり注意を払わずにどこかからカット&ペーストしたことを認めます。ここに問題がある場合は、ズボンを下ろしたままテーブルを回ります。
void printme(void *c, size_t n)
{
unsigned char *t = c;
if (c == NULL)
return;
while (n > 0) {
int q;
--n;
for(q = 0x80; q; q >>= 1)
printf("%x", !!(t[n] & q));
printf(" ");
}
printf("\n");
}