Обход ограничения reinterpret cast с помощью constexpr

В c++11, a constexpr выражение не может содержать переиначить бросает. Например, если кто-то хочет манипулировать битами в числе с плавающей запятой, скажем, чтобы найти мантиссу числа:

constexpr unsigned int mantissa(float x) { 
    return ((*(unsigned int*)&x << 9) >> 9); 
};

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

есть ли какой-либо умный способ получить вокруг этого ограничения?

2 ответов


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

это не портативный.

вы, вероятно, знаете о том, что ваш код вызывает неопределенное поведение, так как вы разыменовываете указатель типа и, таким образом, нарушаете строгое сглаживание. Более того, поскольку C++14, операции, которые вызывали бы неопределенное поведение, больше не являются постоянными выражениями и поэтому должны создавать компилятор ошибка.

что вы пытаетесь сделать, это псевдоним float объект с интегральным glvalue. Первый шаг-получить это glvalue; второй - выполнить преобразование lvalue в rvalue.

в C++14 первый шаг невозможно выполнить в постоянных выражениях. reinterpret_cast категорически запрещено. И бросает в и из void*, как static_cast<char const*>(static_cast<void const*>(&x)), тоже не работает (N3797, [expr.const] / 2*):

- преобразование из типа cv void * к типу указатель-объект;

имейте в виду, что c-стиль бросания как (char*) сводится к либо static_cast или reinterpret_cast чьи ограничения перечислены выше. (unsigned*)&x поэтому сводится к reinterpret_cast<unsigned*>(&x) и не работает.

в C++11 приведение к void const* а потом char const* не представляет проблемы (согласно стандарту; Clang все еще жалуется на последнее). Именующее-для-правосторонним значением преобразования тем не менее:

преобразование lvalue в rvalue (4.1), если оно не применяется к
- a glvalue Интеграл или тип перечисления, который относится к энергонезависимой объект const с предшествующей инициализацией, инициализированный постоянное выражение, или
- glvalue литерального типа, который ссылается на энергонезависимый объект, определенный с помощью constexpr, или это относится к под-объект такого объекта, или
- glvalue литерального типа, который относится к энергонезависимый временный объект, срок службы которого не закончено, инициализировано постоянным выражением;

первые две пули не могут применяться здесь; ни один не имеет char/unsigned / etc. объект был инициализирован precedingly, не мы определяем любой такой объект с constexpr.

третья пуля также не применяется. Если мы напишем

char ch = *(char const*)(void const*)&x;

мы не создаем


ваш конкретный пример получения мантиссы float number на самом деле довольно просто реализовать для цифры без каламбура вообще, и, таким образом, реализовать его в constexpr моды. Единственная проблема будет, когда вы хотите взломать NaNs.

так как вы уже полагаетесь на float являясь binary32 из IEEE 754 мы можем предположить то же самое, но по - другому-представить результаты. См. следующий код:

#include <limits>
constexpr float abs(float x) { return x<0 ? -x : x; }

constexpr int exponent(float x)
{
    return abs(x)>=2 ? exponent(x/2)+1 :
           abs(x)<1  ? exponent(x*2)-1 : 0;
}

constexpr float scalbn(float value, int exponent)
{
    return exponent==0 ? value : exponent>0 ? scalbn(value*2,exponent-1) :
                                              scalbn(value/2,exponent+1);
}

constexpr unsigned mantissa(float x)
{
    return abs(x)<std::numeric_limits<float>::infinity() ?
                // remove hidden 1 and bias the exponent to get integer
                scalbn(scalbn(abs(x),-exponent(x))-1,23) : 0;
}

#include <iostream>
#include <iomanip>
#include <cstring>

int main()
{
    constexpr float x=-235.23526f;
    std::cout << std::hex << std::setfill('0');
    // Show non-constexpr result to compare with
    unsigned val; std::memcpy(&val,&x,sizeof val);
    std::cout << std::setw(8) << (val&0x7fffff) << "\n";
    // Now the sought-for constexpr result
    constexpr auto constexprMantissa=mantissa(x);
    std::cout << std::setw(8) << constexprMantissa << "\n";
}

посмотреть ее жить демо.