Безопасно ли переносить двойное число через int в C ++ 03?

РЕДАКТИРОВАТЬ: пропустите ниже горизонтальное правило для моей новой версии вопроса.

Предполагая только, что sizeof (double) * CHAR_BITS <= 64, всегда ли выполняется утверждение в следующей программе, гарантированное C ++ 03?

union u {
std::int64_t m_int;
double       m_double;
};

int main()
{
double d = get_a_random_double();

u u1;
u1.m_double = d;

u u2;
u2.m_int = u1.m_int;

assert(u2.m_double == d);
}

Я обеспокоен тем, что стандарт может не гарантировать, что копия значения int64_t сохраняет все биты, которые мы используем для хранения двойного числа.

С другой стороны, я уверен, что следующая альтернатива, которая использует символы вместо unt64_t, гарантируется стандартом, чтобы всегда удовлетворять утверждению:

struct chars {
char m_x[sizeof(double)];
};

union u {
chars  m_chars;
double m_double;
};

int main()
{
double d = get_a_random_double();

u u1;
u1.m_double = d;

u u2;
u2.m_chars = u1.m_chars;

assert(u2.m_double == d);
}

Ты согласен?


РЕДАКТИРОВАТЬ:

Хорошо, я должен признать, что преобразования посредством объединения не поддерживаются стандартом.

Так что насчет этого, где я пытаюсь перенести двойной через последовательность символов без использования объединения:

int main()
{
double d, e;
char c[sizeof(double)];

char* p_d = static_cast<char*>(static_cast<void*>(&d));
char* p_e = static_cast<char*>(static_cast<void*>(&e));

d = get_a_random_double();

// double -> chars
std::copy(p_d, p_d+sizeof(double), c);

// chars -> double
std::copy(c, c+sizeof(double), p_e);

assert(e == d);
}

Это гарантировано стандартом, чтобы всегда удовлетворять утверждению?

РЕДАКТИРОВАТЬ: Да! См. C ++ 11 3.9 / 2 «Типы»

Как насчет этого следующего, где я пытаюсь транспортировать двойной желоб через int64_t без использования союзов.

Я знаю, что int64_t не является частью c ++ 03, поэтому давайте вместо этого поговорим о c ++ 11.

Снова обратите внимание, что я предполагаю, что sizeof (double) * CHAR_BITS <= 64

int main()
{
double d, e;
std::int64_t i, j;
char c[sizeof(std::int64_t)];

char* p_d = static_cast<char*>(static_cast<void*>(&d));
char* p_e = static_cast<char*>(static_cast<void*>(&e));
char* p_i = static_cast<char*>(static_cast<void*>(&i));
char* p_j = static_cast<char*>(static_cast<void*>(&j));

d = get_a_random_double();

// double -> chars -> std::int64_t
std::copy(p_d, p_d+sizeof(double), c);
std::copy(c, c+sizeof(std::int64_t), p_i);

// std::int64_t -> std::int64_t
j = i; // <------------ Are all bits preserved here?

// std::int64_t -> chars -> double
std::copy(p_j, p_j+sizeof(std::int64_t), c);
std::copy(c, c+sizeof(double), p_e);

assert(e == d);
}

Как и прежде, меня беспокоит, может ли копирование одного из стандартных целочисленных типов (std :: int64_t) «испортить» некоторые биты (например, некоторые биты, которые не участвуют в представлении целочисленных значений). Или гарантируется ли целочисленное присваивание, чтобы всегда точно копировать все биты байтов, которые занимает целое число?

0

Решение

Нет, на самом деле это неопределенное поведение:

u u1;
u1.m_double = d;

u u2;
u2.m_int = u1.m_int;

Вы не можете установить u1.m_double и читать u1.m_int,

Только один активный член профсоюза допускается одновременно.

4

Другие решения

Сам язык не гарантирует, что вы можете использовать любой другой тип, кроме char или же unsigned char указатели для доступа к другим типам данных — и даже это довольно ограничено.

Все зависит от того, насколько переносимым должен быть ваш код. У некоторых машин есть причудливые способы обработки чисел с плавающей запятой, и их использование может привести к плохим результатам.

На практике, MOST-процессоры будут прекрасно работать с тем, что вы только что сделали, и большинство компиляторов будут рады создать «правильный» код для наложения 64-разрядного целого числа на 64-разрядное число с плавающей запятой и передачи его таким образом, чтобы путь.

Проблема возникает, когда вы пытаетесь запустить это на DSP, старом оборудовании IBM, Cray YMP или чем-то еще, что немного необычно. Или если вы начнете использовать очень непроверенные компиляторы.

Таким образом, в некоторой степени все сводится к тому, «насколько вам нужен портативный компьютер», и насколько хорошо тестируется ваше программное обеспечение, чтобы оно не проскальзывало, как странная ошибка, которая возникает только по вторникам в месяцах с буквой R в названии, и только в дни, которые равномерно делятся на 7 и 3, при полной луне? [И неизбежно только вашим самым важным клиентом, который находится в стране, которая находится примерно в 12 часах от вашего часового пояса].

3

Все типы с фиксированными размерами c ++ 11. До этого стандарт определял только минимальный количество битов, которое должна содержать переменная (большинство из них гарантируют, что она будет по крайней мере такой же большой, как и предыдущая …). так std::int64_t будет неопределенным в c ++ 03, и вы не можете полагаться на любые размеры других целых (включая char!).


Кроме того, как указывает Лучиан Григоре: письмо одному члену профсоюза и чтение другого не определено (любым) стандартом. Тем не менее, хотя вам не гарантируется, что написанный вами код будет работать, большинство компиляторов интерпретируют его так, как вы хотели.

1
По вопросам рекламы [email protected]