Предположим, что у меня есть гарантии, что float
- IEEE 754 binary32. Учитывая битовый шаблон, который соответствует допустимому float, хранящемуся в std::uint32_t
, как он переинтерпретирует его как float
наиболее эффективным стандартным образом?
float reinterpret_as_float(std::uint32_t ui) {
return /* apply sorcery to ui */;
}
У меня есть несколько способов, которые я знаю/подозреваю/предполагаю, имеют некоторые проблемы:
-
Через
reinterpret_cast
,float reinterpret_as_float(std::uint32_t ui) { return reinterpret_cast<float&>(ui); }
или эквивалентно
float reinterpret_as_float(std::uint32_t ui) { return *reinterpret_cast<float*>(&ui); }
который страдает от проблем с псевдонимом.
-
Через
union
,float reinterpret_as_float(std::uint32_t ui) { union { std::uint32_t ui; float f; } u = {ui}; return u.f; }
который на самом деле не является законным, так как ему разрешено читать только от последнего, написанного до члена. Тем не менее, похоже, некоторые компиляторы (gcc) позволяют это.
-
Через
std::memcpy
,float reinterpret_as_float(std::uint32_t ui) { float f; std::memcpy(&f, &ui, 4); return f; }
который AFAIK является законным, но вызов функции для копирования одного слова кажется расточительным, хотя он может быть оптимизирован.
-
Через
reinterpret_cast
доchar*
и копированияfloat reinterpret_as_float(std::uint32_t ui) { char* uip = reinterpret_cast<char*>(&ui); float f; char* fp = reinterpret_cast<char*>(&f); for (int i = 0; i < 4; ++i) { fp[i] = uip[i]; } return f; }
который AFAIK также является законным, поскольку указатели
char
освобождаются от проблем с псевдонимом, а ручной цикл копирования байтов сохраняет возможный вызов функции. Цикл, безусловно, будет разворачиваться, но 4, возможно, отдельные однобайтовые нагрузки/хранилища являются тревожными, я понятия не имею, оптимизируется ли это однократная загрузка/хранение по четыре байта.
4
- лучшее, что я смог придумать.
До сих пор я исправляю? Есть ли лучший способ сделать это, особенно тот, который гарантирует единую загрузку/хранение?