On 2016-06-13 22:51, Joseph Myers wrote:
On Mon, 13 Jun 2016, Alexander Cherepanov wrote:

Thanks for the info. IMHO this part of DR 260 has even more serious
consequences than the part about pointer provenance. It effectively prohibits
manual byte-by-byte (or any non-atomic) copying of objects for types like long
double. If an implementation decides to normalize a value in a variable during
copying it will see an inconsistent representation, e.g. a trap
representation. It's a sure way to get total garbage. I don't know if allowing

No, that's not the case; even if representations can change during
byte-by-byte copying, such copying of long double values is *still* safe.
All long double values for x86 long double have exactly one valid
representation in the value bits, and if the padding bits change during
copying it doesn't matter; it's only representations that are already trap
representations (unnormals, pseudo-* etc.) that might be interpreted
inconsistently.

The problem is that parts of representations of two different ordinary values can form a trap representation.

Suppose x = 1.0 and y = 0.0, i.e. they have the following representations (from high bytes to low bytes):

        padding      sign        int & frac
                     & exp
   |---------------| |---| |---------------------|
x: 00 00 00 00 00 00 3f ff 80 00 00 00 00 00 00 00
y: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Suppose that we copy from x to y byte-by-byte starting from high bytes. And suppose the normalization kicks in after copying 8 bytes. We have already copied the sign and the exponent but haven't yet overwritten the 'Integer' bit of Significand so we have the following representation:

z: 00 00 00 00 00 00 3f ff 00 00 00 00 00 00 00 00

This is an unnormal and current gcc normalization converts it into 0.0 throwing the exponent away. Copying the remaining 8 bytes leads to a pseudo-denormal:

w: 00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00

But this is already a minor detail.

The code to see how gcc normalizes 'z':

----------------------------------------------------------------------
#include <string.h>
#include <stdio.h>

int main()
{
  long double d0, d;

memcpy(&d0, "\x00\x00\x00\x00\x00\x00\x00\x00\xff\x3f\x00\x00\x00\x00\x00\x00", sizeof d0);
  d = d0;

  printf("d = %Lf\n", d);
for (unsigned char *p = (unsigned char *)&d + sizeof d; p > (unsigned char *)&d;)
    printf("%02x ", *--p);
  printf("\n");
}
----------------------------------------------------------------------

Results:

----------------------------------------------------------------------
$ gcc -std=c11 -pedantic -Wall -Wextra -O3 test.c && ./a.out
d = 0.000000
00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00
----------------------------------------------------------------------

gcc version: gcc (GCC) 7.0.0 20160613 (experimental)

--
Alexander Cherepanov

Reply via email to