TLDR; Does the following code invoke undefined (or unspecified) behaviour ?
#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;
}
On my x86-64 machine, the output depends on the specific optimization flags passed to the compiler (gcc-4.8.0, -O0 vs -O1).
With -O0, I get
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
While with -O1, I get
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
Please note the extra 1 in the second last line. Also, uncommenting the print instructions after the memset makes that 1 disappear. This seems to rely on two facts:
- long double is padded, i.e., sizeof(long double) = 16 but only 10 bytes are used.
- the call to memset might be optimized away
- the padding bits of the long doubles might change without notice, i.e. floating point operations on value1 and value2 seems to scramble the padding bits.
I'm compiling with -std=c99 -Wall -Wextra -Wpedantic
and get no warnings so I'm not sure this is a case of strict aliasing violation (but it might well be). Passing -fno-strict-aliasing
doesn't change a thing.
The context is a bug found in HDF5 library described here. HDF5 does a some bit fiddling to figure out the native bit representation of floating point types, but it gets confused if the padding bits do not stay zero.
So:
- Is this undefined behaviour?
- Is this a strict aliasing violation?
Thanks.
edit: This is the code for printme. I admit I had just cut&pasted from somewhere without paying too much attention to it. If the fault is in here I'll go around the table with pants down.
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");
}