РЕДАКТИРОВАТЬ: пропустите ниже горизонтальное правило для моей новой версии вопроса.
Предполагая только, что 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) «испортить» некоторые биты (например, некоторые биты, которые не участвуют в представлении целочисленных значений). Или гарантируется ли целочисленное присваивание, чтобы всегда точно копировать все биты байтов, которые занимает целое число?
Нет, на самом деле это неопределенное поведение:
u u1;
u1.m_double = d;
u u2;
u2.m_int = u1.m_int;
Вы не можете установить u1.m_double
и читать u1.m_int
,
Только один активный член профсоюза допускается одновременно.
Сам язык не гарантирует, что вы можете использовать любой другой тип, кроме char
или же unsigned char
указатели для доступа к другим типам данных — и даже это довольно ограничено.
Все зависит от того, насколько переносимым должен быть ваш код. У некоторых машин есть причудливые способы обработки чисел с плавающей запятой, и их использование может привести к плохим результатам.
На практике, MOST-процессоры будут прекрасно работать с тем, что вы только что сделали, и большинство компиляторов будут рады создать «правильный» код для наложения 64-разрядного целого числа на 64-разрядное число с плавающей запятой и передачи его таким образом, чтобы путь.
Проблема возникает, когда вы пытаетесь запустить это на DSP, старом оборудовании IBM, Cray YMP или чем-то еще, что немного необычно. Или если вы начнете использовать очень непроверенные компиляторы.
Таким образом, в некоторой степени все сводится к тому, «насколько вам нужен портативный компьютер», и насколько хорошо тестируется ваше программное обеспечение, чтобы оно не проскальзывало, как странная ошибка, которая возникает только по вторникам в месяцах с буквой R в названии, и только в дни, которые равномерно делятся на 7 и 3, при полной луне? [И неизбежно только вашим самым важным клиентом, который находится в стране, которая находится примерно в 12 часах от вашего часового пояса].
Все типы с фиксированными размерами c ++ 11. До этого стандарт определял только минимальный количество битов, которое должна содержать переменная (большинство из них гарантируют, что она будет по крайней мере такой же большой, как и предыдущая …). так std::int64_t
будет неопределенным в c ++ 03, и вы не можете полагаться на любые размеры других целых (включая char!).
Кроме того, как указывает Лучиан Григоре: письмо одному члену профсоюза и чтение другого не определено (любым) стандартом. Тем не менее, хотя вам не гарантируется, что написанный вами код будет работать, большинство компиляторов интерпретируют его так, как вы хотели.