Constexpr функция на с++11

Здравствуйте!

Имеется constexpr функция возведения числа 2.0 (float) в целочисленную степень:

static constexpr float factor(const int8_t power) {
  int32_t tmp = 0x3F800000 + (static_cast<int32_t>(power) << 23);
  return *reinterpret_cast<float *>(&tmp);
}

Функция увеличивает или уменьшает экспоненту числа на значение power, что равносильно возведению числа 2 в эту степень.
Функция собирается только с с++14.

  1. Можно как-то преобразовать данную функцию, чтобы она собиралась под с++11?
  2. Как правильно приводить типы в 3 строке, чтобы убрать предупреждение компилятора
    warning: dereferencing type-punned pointer will break strict-aliasing rules?

union и memcpy точно с этим справятся

С union не собирается:

static  constexpr float power(const int8_t value) {
    union {
      float f;
      int32_t i32;
      int16_t i16[2];
    } tmp;
    
    tmp.f = 1.0;
    tmp.i16[1] += static_cast<int16_t>(value) << 7;

    return tmp.f;
  }

Ругается на неинициализированную переменную
error: uninitialized variable ‘tmp’ in ‘constexpr’ function
Попробую с memcopy.
А чтоб с с++11 собрать варианты есть?

static constexpr float factor(const int8_t power) {
union { int32_t i; float f; } converter;
converter.i = 0x3F800000 + (static_cast<int32_t>(power) << 23);
return converter.f; }

?

Если инициализировать, то собирается

  static constexpr float factor3(const int8_t power) {
    union {
      int32_t i;
      float f;
    } converter {0};
    converter.i = 0x3F800000 + (static_cast<int32_t>(power) << 23);
    return converter.f;
  }

значит вы сами ответили на свой вопрос)))

А чтоб с с++11 собиралось есть варианты? Не могу придумать как это все в 1 return сделать.

Можно, конечно. Проблема в том, что в С++11 constexpr функция обязана состоять только из одного оператора return. Надо просто переписать функцию таким образом. Перепишете? Или показать?

Правильно - никак, т.к. такое преобразование является нарушением правил strict-aliasing. Можно, конечно, обмануть, чтобы заткнулась (но это неправильно!). Ну, или просто отключить strict-aliasing (если оно у вас отключается), что честнее

Евгений Петрович, покажите, если не сложно. Я не знаю как это делать.

конечно есть, и их несколько, об этом многие знают

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

если интересно знаю ли я, то ответ нет, удачи)))

static constexpr float factor(const int8_t power) {
    // Для C++11 используем рекурсивный подход
    return power == 0 ? 1.0f : 
           power > 0 ? 2.0f * factor(power - 1) : 
           factor(power + 1) / 2.0f;
}

в твоем случае достаточно просто отключить этот варнинг в компиляторе. компилятор ругается, что из-за такого приведения типов он не может в полную развернуться в смысле оптимизации.

С++ создает геморой на пустом месте :slight_smile:

Ну правильно. Ты ж только 16 бит проинитил.

Вы абсолютно правы! В C++11 constexpr функции действительно могут состоять только из одного оператора return. Вот переписанная функция:

Вариант 1: Использование тернарного оператора

static constexpr float factor(const int8_t power) {
    return *reinterpret_cast<float*>(&(int32_t{0x3F800000 + (static_cast<int32_t>(power) << 23)}));
}

Вариант 2: Более безопасный с union

static constexpr float factor(const int8_t power) {
    return (union { int32_t i; float f; }){0x3F800000 + (static_cast<int32_t>(power) << 23)}.f;
}

Вариант 3: С использованием временного объекта

static constexpr float factor(const int8_t power) {
    return *reinterpret_cast<const float*>(
        static_cast<const void*>(
            &static_cast<const int32_t>(0x3F800000 + (static_cast<int32_t>(power) << 23))
        )
    );
}

Вариант 4: Математический подход (самый безопасный)

static constexpr float factor(const int8_t power) {
    return power == 0 ? 1.0f : 
           power > 0 ? 2.0f * factor(power - 1) : 
           0.5f * factor(power + 1);
}

Рекомендация:

Вариант 2 с union - наиболее предпочтителен, так как:

  • Соответствует C++11 требованиям
  • Избегает strict-aliasing проблем
  • Сохраняет оригинальную логику работы
  • Компилируется без предупреждений

Вариант 4 также хорош для полной переносимости, но может иметь ограничения по глубине рекурсии для больших значений power.

Вариант 1 я сам пробовал, у меня не собрался. Компилятор ругался, что выражение не и является lvalue и получить его адрес не удается.
Вариант 2 вечером попробую. Должен собраться.
Вариант 4 тоже сам использую, но в данном случае нужно обойтись без рекурсии.
Доберусь до дома и проверю все варианты. Возможно и в 1 варианте я где-то с синтаксисом намудрил.

это леко:

return ({ int ReturnValue;  
 /*твой код здесь, сколько угодно, циклы, там, всякие, то-сё.
     в переменную ReturnValue заносим значение, которое надо возвернуть
*/
   ; ReturnValue;});

О, идея пришла в голову.
Я не знаю, сработает или нет, но попробовать-то можно :).
Стирай constexpr, пиши вместо него ..attribute..((const)) (две точки это два подчеркивания, не зна., как тут два подчеркивания написать)

А если эта функция будет использоваться с константами в качестве аргумента, а не с переменной цикла, например, то вообще непонятно, зачем все это было городить? Компилятор при компиляции все сам вычислит безо всяких ухищрений.

Вы где и как эту функцию использовать собираетесь?

еще пища для размышлений

#include <cstring>
static constexpr float factor(const int8_t power) {
int32_t tmp = 0x3F800000 + (static_cast<int32_t>(power) << 23);
float result; 
std::memcpy(&result, &tmp, sizeof(result)); 
return result; }