繁体   English   中英

C ++便携式浮点位表示法?

[英]C++ Portable Floating-Point Bit Representation?

是否有符合C ++标准的方法来确定编译时(或运行时,作为替代)“浮点数”,“双精度”和“长双精度”的结构?

如果我假设std::numeric_limits< T >::is_iec559 == truestd::numeric_limits< T >::radix == 2 ,我怀疑可以通过以下规则进行:

  • 前X位是有效位。
  • 下一个Y位是指数。
  • 最后1位是符号位。

含糊的以下表达式:

  • 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

背景 :我正在努力克服两个问题:

  • 设置/获取有效位。
  • 确定“ long double”的结尾在哪里,以便我知道不读取将具有未初始化内存的隐式填充位。

简而言之,没有。 如果std::numeric_limits<T>::is_iec559 ,则std::numeric_limits<T>::is_iec559知道T的格式:您仍然必须确定字节顺序。 除此之外,所有的赌注都没有了。 (例如,我知道仍在使用的其他格式甚至都不是基数2:例如,IBM大型机使用基数16。)IEC浮点的“标准”排列在高阶位上带有符号,然后是指数,以及低阶位的尾数; 例如,如果您可以成功地将其查看为uint64_t (通过memcpyreinterpret_castunion —保证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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM