Существует ли совместимый со стандартами C ++ способ определения структуры типа «float», «double» и «long double» во время компиляции (или во время выполнения, в качестве альтернативы)?
Если я предполагаю std::numeric_limits< T >::is_iec559 == true
а также std::numeric_limits< T >::radix == 2
Я подозреваю, что это возможно по следующим правилам:
со следующими выражениями смутно, как:
size_t num_significand_bits = std::numeric_limits< T >::digits;
size_t num_exponent_bits = log2( 2 * std::numeric_limits< T >::max_exponent );
size_t num_sign_bits = 1u;
кроме я знаю
std::numeric_limits< T >::digits
включает в себя «целочисленный бит», независимо от того, действительно ли формат представляет его явным образом, поэтому я не знаю, как программно обнаружить и откорректировать это.std::numeric_limits< T >::max_exponent
всегда 2^(num_exponent_bits)/2
,ФонЯ пытаюсь преодолеть две проблемы:
Короче нет. Если std::numeric_limits<T>::is_iec559
, затем вы
знать формат T
более или менее: вам все равно придется
определить порядок байтов. Во всем остальном все ставки сняты.
(Другие известные мне форматы даже не используются.
база 2: мэйнфреймы IBM используют базу 16, например.)
«стандартное» расположение плавающей запятой МЭК имеет знак
старший разряд, затем показатель степени и мантисса на
биты младшего разряда; если вы можете успешно просмотреть его как
uint64_t
Например, (через memcpy
, reinterpret_cast
или же
union
— mempy работает гарантированно, но меньше
эффективнее двух других), то:
за double
:
uint64_t tmp;
memcpy( &tmp, &theDouble, sizeof( double ) );
bool isNeg = (tmp & 0x8000000000000000) != 0;
int exp = (int)( (tmp & 0x7FF0000000000000) >> 52 ) - 1022 - 53;
long mant = (tmp & 0x000FFFFFFFFFFFFF) | 0x0010000000000000;
для `float:
uint32_t tmp;
memcpy( &tmp, &theFloat, sizeof( float ) );
bool isNeg = (tmp & 0x80000000) != 0;
int exp = (int)( (tmp & 0x7F800000) >> 23 ) - 126 - 24 );
long mant = (tmp & 0x007FFFFF) | 0x00800000;
Что касается long double
Хуже, потому что разные
компиляторы относятся к этому по-разному, даже на одной машине.
Номинально, это десять байтов, но по причинам выравнивания
факт 12 или 16. Или просто синоним double
, Если его
больше 10 байт я считать Вы можете рассчитывать на упаковку
в первые 10 байтов, так что &myLongDouble
дает
адрес 10-байтового значения. Но, вообще говоря, я бы избегал
long double
,
Я бы сказал, что единственный переносимый способ — хранить число в виде строки. Это не полагается на «интерпретацию битовых паттернов»
Даже если вы знаете, сколько битов что-то есть, это не значит, что оно имеет одно и то же представление — показатель степени на основе нуля или смещенный. Есть ли невидимая 1 в передней части мантиссы? То же самое относится ко всем остальным частям номера. И это еще хуже для кодированных BCD или «шестнадцатеричных» операций с плавающей запятой — они доступны в некоторых архитектурах …
Если вас беспокоят неинициализированные биты в структуре (класс, массив и т. Д.), Используйте memset, чтобы установить всю структуру на ноль [или другое известное значение].
Для потомков это то, чем я в конечном итоге занимаюсь.
Чтобы сгенерировать и проверить мои значения сигнализации NEE IEEE-754, я использую этот шаблон для «float» и «double».
#include <cstdint> // uint32_t, uint64_t
#include <limits> // numeric_limits
union IEEE754_Float_Union
{
float value;
uint32_t bits;
};
float generate_IEEE754_float()
{
IEEE754_Float_Union u = { -std::numeric_limits< float >::signaling_NaN() };
size_t const num_significand_bits_to_set = std::numeric_limits< float >::digits
- 1 // implicit "integer-bit"- 1; // the "signaling-bit"u.bits |= ( static_cast< uint32_t >( 1 ) << num_significand_bits_to_set ) - 1;
return u.value;
}
bool test_IEEE754_float( float const& a_r_val )
{
IEEE754_Float_Union const u = { a_r_val };
IEEE754_Float_Union const expected_u = { generate_IEEE754_float() };
return u.bits == expected_u.bits;
}
Для «long double» я использую функции «double» с приведением типов. В частности, я генерирую значение «double» и преобразую его в «long double», прежде чем оно возвращается, и я проверяю «long double», приводя значение «double», а затем проверяю это значение. Моя идея заключается в том, что, хотя формат «long double» может варьироваться, приведение «double» к «long double», затем последующее приведение его к «double» должно быть последовательным (то есть не терять никакой информации).