[英]C++ Portable Floating-Point Bit Representation?
是否有符合C ++标准的方法来确定编译时(或运行时,作为替代)“浮点数”,“双精度”和“长双精度”的结构?
如果我假设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
,则std::numeric_limits<T>::is_iec559
知道T
的格式:您仍然必须确定字节顺序。 除此之外,所有的赌注都没有了。 (例如,我知道仍在使用的其他格式甚至都不是基数2:例如,IBM大型机使用基数16。)IEC浮点的“标准”排列在高阶位上带有符号,然后是指数,以及低阶位的尾数; 例如,如果您可以成功地将其查看为uint64_t
(通过memcpy
, reinterpret_cast
或union
—保证memcpy
可以工作,但效率不如其他两个),则:
对于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将整个结构设置为零[或其他已知值]。
为了后代,这就是我最后要做的。
为了生成并测试我的IEEE-754信令NaN值,我将此模式用于“浮动”和“双精度”。
#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”,然后将其返回,然后通过转换为“ double”然后测试该值来测试“ long double”。 我的想法是,虽然“长双精度”格式可以变化,但将“双精度”转换为“长双精度”,然后稍后将其转换回“双精度”应该是一致的(即,不要丢失任何信息。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.