Введите каламбур в константном / статическом инициализаторе (построение константы с плавающей запятой из битов)

Некоторые языки (например, Rust, Zig, GLSL, HLSL) имеют встроенные методы для создания плавающего типа из битов, представленных в виде целого числа без знака. Однако C и C++ не имеют для этого стандартных функций.

С C99 мы можем использовать анонимные объединения с инициализацией члена для реализации макроса каламбура типа с тем же эффектом:

#define FLOAT_FROM_BITS(U,F,b) (((union{U u; F f;}){.u=(b)}).f)
#define FLOAT32_FROM_BITS(i) FLOAT_FROM_BITS(uint32_t, float,  i)
#define FLOAT64_FROM_BITS(i) FLOAT_FROM_BITS(uint64_t, double, i)

который впоследствии можно использовать для инициализации const/static с помощью. Какой самый элегантный способ сделать это на С++, чтобы его также можно было использовать для статической инициализации?

Какую версию C++ вы можете использовать? C++20 поставляется с std::bit_cast

NathanOliver 16.05.2022 22:44

@NathanOliver: C++20 на самом деле является моим текущим целевым профилем. Я не знал о существовании std::bit_cast. Однако ради людей, которые еще не могут его использовать, я хотел бы увидеть другие подходы, если они возможны.

datenwolf 16.05.2022 22:47

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

Goswin von Brederlow 16.05.2022 23:20
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
1
3
49
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Элегантный способ — использовать std::bit_cast:

std::uint64_t i = example_value();
auto d = std::bit_cast<double>(i);
Ответ принят как подходящий

Если вы можете использовать С++ 20 или выше, используйте std::bit_cast, например

auto myvar = std::bit_cast<type_to_cast_to>(value_to_cast);

Если вы хотите поддерживать более старые версии, вы можете сделать то же самое, используя std::memcpy для копирования байтов из одного типа в другой. Это даст вам такую ​​​​функцию, как

template <class To, class From>
To bit_cast(const From& src)
{
    To dst;
    std::memcpy(&dst, &src, sizeof(To));
    return dst;
}

Но работает ли memcpy со статической инициализацией? Например. если бы я хотел написать что-то вроде float const my_magic_constant = FLOAT_FROM_BITS(…); в идеале, когда компилятор просто выдавал необработанную константу в сгенерированном коде.

datenwolf 16.05.2022 22:52

Что ж, мне нужна инициализация constexpr.

datenwolf 16.05.2022 22:53

@datenwolf В этом случае это будет работать, но, скорее всего, функция будет работать во время выполнения, поэтому в ваш двоичный файл не запекается константа.

NathanOliver 16.05.2022 22:53

@datenwolf В таком случае вам нужно std::bit_cast. Возможно, вы сможете сделать свой собственный, но вам нужно будет изучить, какие встроенные функции доступны в вашей реализации, и вы сможете создать свою собственную версию, используя их.

NathanOliver 16.05.2022 22:55

Ну, это облом; рассматриваемая константа будет применяться в самом внутреннем цикле некоторой функции преобразования данных DSP. Я как бы не хочу загружать эту константу из памяти (хотя, я думаю, когда она находится в регистре, это не имеет большого значения).

datenwolf 16.05.2022 22:55

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

NathanOliver 16.05.2022 22:56

@datenwolf Как инициализация constexpr влияет на «загрузку из памяти»?

eerorika 16.05.2022 22:58

@eerorika: значение, которое я хочу применить, представляет собой фиксированную константу с определенным битовым шаблоном, который известен во время компиляции. Строго говоря, во время генерации кода компилятор может просто вставить этот конкретный битовый шаблон в сгенерированный код в качестве операнда с постоянным значением. Что избавило бы от загрузки одного слова из памяти. Теперь, поскольку эта константа используется во всем самом внутреннем цикле, который происходит только один раз. Но представьте, что алгоритм состоит из большого количества таких констант (больше, чем доступных регистров), тогда вы рискуете столкнуться с проблемами задержки памяти (в случае промахов кеша L1).

datenwolf 16.05.2022 23:03

@eerorika: Для практического примера таких «магических» констант найдите алгоритм быстрый обратный квадратный корень (иногда ошибочно приписываемый Кармаку, но он намного старше, и мы не знаем, кто именно был безумным гением, который нашел эту магическую константу в первую очередь) ).

datenwolf 16.05.2022 23:04

@datenwolf Я провел несколько экспериментов, и оказалось, что GCC никогда не использует mov eax, CONSTANT с поплавками. Он использует его только с целыми числами. Даже со значением constexpr он использует movss xmm0, DWORD PTR .LCx[rip] для загрузки значения в регистр. Единственная разница с использованием std::memcpy заключается в том, что оно копирует значение в память при запуске (с использованием того же трюка mov DWORD PTR c1[rip], CONSTANT) вместо использования сегмента только для чтения.

eerorika 16.05.2022 23:27

@eerorika: я могу подтвердить ваши результаты для x86[_64] для GCC, CLang и MSVC. Однако для AArch64 и GCC, и Clang будут выдавать немедленные значения для float32, а Clang также для float64, но не для GCC: godbolt.org/z/Y4GTreaqb

datenwolf 17.05.2022 19:45

Другие вопросы по теме