Союз не переосмысливает ценности? - PullRequest
8 голосов
/ 20 сентября 2019

Рассмотрим эту программу:

#include <stdio.h>

union myUnion
{
    int x;
    long double y;
};

int main()
{
    union myUnion a;
    a.x = 5;
    a.y = 3.2;
    printf("%d\n%.2Lf", a.x, a.y);
    return 0;
}

Вывод:

-858993459
3.20

Это нормально, так как член int интерпретируется с использованием некоторых битов члена long double,Однако обратное на самом деле не применяется:

#include <stdio.h>

union myUnion
{
    int x;
    long double y;
};

int main()
{
    union myUnion a;
    a.y = 3.2;
    a.x = 5;
    printf("%d\n%.2Lf", a.x, a.y);
    return 0;
}

Вывод:

5
3.20

Вопрос в том, почему long double не интерпретируется как какое-то значение мусора (так как 4из его байтов должны представлять целое число)?Это не совпадение, программа выдает 3.20 для всех значений a.x, а не только 5.

Ответы [ 4 ]

7 голосов
/ 20 сентября 2019

Однако обратное действительно не применимо

В системе с прямым порядком байтов (младший байт многобайтового значения находится по самому низкому адресу), int будет соответствовать младшим значащим битам мантиссы long double.Вы должны напечатать это long double с большой точностью, чтобы увидеть влияние этого int на эти незначительные цифры.

В большой системе с прямым порядком байтов, такой как блок Power PC, все будет иначе: часть int будет соответствовать самой значимой части long double, перекрывая ее знаковым битом, экспонентой и наиболее значимыми битами мантиссы.Таким образом, изменения в x окажут радикальное влияние на наблюдаемое значение с плавающей точкой, даже если будут напечатаны только несколько значащих цифр.Однако для небольших значений x значение представляется равным нулю.

В системе PPC64 следующая версия программы:

int main(void)
{
    union myUnion a;
    a.y = 3.2;
    int i;
    for (i = 0; i < 1000; i++) {
      a.x = i;
      printf("%d -- %.2Lf\n", a.x, a.y);
    }
    return 0;
}

не печатает ничего, кроме

1 -- 0.0
2 -- 0.0
[...]
999 - 0.0

Это потому, что мы создаем поле экспоненты со всеми нулями, давая значения, близкие к нулю.Однако начальное значение 3.2 полностью забито;у него не только разорваны наименее значимые биты.

6 голосов
/ 20 сентября 2019

Размер long double очень большой.Чтобы увидеть эффект изменения поля x в реализациях, где x совпадает с младшими битами мантиссы y, а другие биты объединения не применяются при изменении через x, вам нужно выведите значение с гораздо более высокой точностью .

4 голосов
/ 20 сентября 2019

Это влияет только на последнюю половину мантиссы.Это не будет иметь никакого значительного значения с количеством цифр, которые вы печатаете.Однако разницу можно увидеть, когда вы печатаете 64 цифры.

Эта программа покажет разницу:

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

union myUnion
{
    int x;
    long double y;
};

int main()
{
    union myUnion a;
    a.y = 3.2;
    a.x = 5;
    printf("%d\n%.64Lf\n", a.x, a.y);
    a.y = 3.2;
    printf("%.64Lf\n", a.y);
    return 0;
}

Мой вывод:

5
3.1999999992549419413918193599855044340074528008699417114257812500
3.2000000000000001776356839400250464677810668945312500000000000000

На основе моегознание 80-битного формата long double позволяет перезаписать половину мантиссы, что не сильно искажает результат, поэтому выводит несколько точные результаты.

Если вы сделали это в моей программе:

a.x = 0;

результат был бы:

0
3.1999999992549419403076171875000000000000000000000000000000000000
3.2000000000000001776356839400250464677810668945312500000000000000

, что немного отличается.

0 голосов
/ 21 сентября 2019

Ответы, опубликованные Mohit Jain, Kaz и JL2210, дают хорошее представление о ваших наблюдениях и дальнейших исследованиях, но имейте в виду, что Стандарт C не гарантирует такого поведения:

6.2.6 Представления типов 6.2.6.1 Общее

6 Когда значение сохраняется в объекте структуры или типа объединения, в том числе в объекте-члене, байты представления объектакоторые соответствуют любым байтам заполнения, принимают неопределенные значения.Значение структуры или объекта объединения никогда не является представлением ловушек, даже если значение члена структуры или объекта объединения может быть представлением ловушек.

7 Когда значение сохраняется в члене объектаобъект типа объединения, байты представления объекта, которые не соответствуют этому члену, но соответствуют другим членам, принимают неопределенные значения.

Как следствие, поведение, описанное в ответах, не гарантируетсяпоскольку все байты элемента long double y могут быть изменены путем установки элемента int x, включая байты, которые не являются частью int.Эти байты могут принимать любое значение, а содержимое y может даже быть значением ловушки, вызывая неопределенное поведение.

Как прокомментировал Kaz, gcc более точен, чем стандарт C: примечания к документации это как обычная практика: распространена практика чтения от члена профсоюза, отличного от того, к которому был недавно написан (называется type-punning ).Даже с -fstrict-aliasing типовое перфорирование разрешено, если доступ к памяти осуществляется через объединительный тип.Эта практика фактически одобряется в Стандарте C начиная с C11, как указано в этом ответе: https://stackoverflow.com/a/11996970/4593267.Тем не менее, в моем прочтении этой сноски все еще нет гарантии, что байты y не являются частью x.

...