【问题标题】:IEEE float hex 424ce027 to float?IEEE浮动十六进制424ce027浮动?
【发布时间】:2011-07-07 02:33:30
【问题描述】:

如果我有一个 IEEE 浮点十六进制 424ce027,如何将其转换为十进制?

unsigned char ptr[] = {0x42,0x4c,0xe0,0x27};

怎么办?

float tmp = 51.218899;

【问题讨论】:

    标签: c++ c hex ieee-754


    【解决方案1】:

    如果你不想假设 endian 并打破各种别名规则,这种事情很管用:

    union IntFloat {
      uint32_t i;
      float f;
    };
    ...
    union IntFloat val;
    val.i = 0x424ce027;
    printf("%f\n", val.f);
    

    仍然假设 'float' 是 32 位的,并且您的机器符合 IEEE-754 标准,并且具有与整数匹配的字节序,但我们可能不再那么迂腐了。

    【讨论】:

    • 联合的使用是GCC扩展。访问未写入的联合成员是UB。
    • @wilx:在 C++ 和(我认为)C89 中。在 C99 中,6.5.2.3/3 不禁止访问不同的成员,并且脚注指出它是允许的。但是通过这样做,除了任何类型双关对存储表示的假设之外,您还对联合的布局做出了假设。如果所有假设都是正确的(并且可以检查布局-实际上我认为可以保证每个成员都从联合的开头开始,我只是没有找到参考),那么就定义了行为。跨度>
    • @Steve Jessop:C99 不是 C++,它与手头的事情无关。它是 C++ 中的 UB。
    • @wilx:问题被标记为 C 与标记为 C++ 一样多。
    • 老实说,我不知道它在 C++ 中是未定义的。这似乎是一种定义规范的懒惰方式。至少实现定义会推断出意图。未定义只是意味着编译器可以(而且现在会)做它喜欢的任何事情。耻辱。无论如何,这里还有很多其他的假设,比如主机上的 IEEE754。否则,唯一 100% 可移植的方法是编写一个 IEEE754 浮点加载函数。
    【解决方案2】:

    对于转换整数值,我的做法与 John Ripley 的回答非常相似,但机制略有不同。相同的注意事项 - float 和 int 必须具有相同的大小和字节序,如果要将初始十六进制值视为 IEEE,则​​ float 必须是 IEEE:

    float tmp;
    unsigned int src = 0x424ce027;
    std::memcpy(&tmp, &src, sizeof tmp);
    

    你问如何转换为浮点数,然后如何转换为十进制。 浮点数不是小数。要将浮点数转换为十进制字符串,您需要类似printf("%g", tmp);

    如果您从 unsigned char 数组而不是 int 数组开始,则从数组中直接复制的任何内容都需要使用与您的平台上相同的字节序填充数组。你的阵列是大端的,但英特尔是小端的。因此,如果您知道平台的字节序与数组的字节序相反,那么您可以像 JohnB 的回答那样 reverse 数组。如果你知道数组是大端的,并且你不知道你的平台是什么端,你可以这样做(假设 char 数组中每个字符 8 位):

    unsigned int src = 0;
    for (int i = 0; i < sizeof src; ++i) {
        src = (src << 8) + ptr[i];
    }
    

    然后像以前一样继续。

    【讨论】:

      【解决方案3】:

      唯一可移植的方法是分析位模式并手动形成浮点数,以继承 ieee-754 浮点数的所有规则:

      #define BIAS 150
      unsigned char ptr[4] = {0x42,0x4c,0xe0,0x27};
      // take care of endianness, which may vary between native float and native int
      uint32_t tmp = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
      uint32_t sign = tmp >> 31;
      uint32_t exp = (tmp >> 23) & 0xff;
      uint32_t mantissa = tmp & ((1 << 23) - 1);
      float result;
      if (exp > 0)
         mantissa |= (1 << 23);                        // Handle denormals && Inf
      
      // all integers < 16777216 can be represented exactly as a float
      // multiply that by a power of two
      
      result = ldexpf((float)mantissa, exp - BIAS);    // in <math.h>
      
      if (exp == 255 && mantissa != 0)  // Produce NaN from Inf-Inf
         result = (result - result);
      
      if (sign) result = -result;       // Flip sign (also handles -0.0f)
      

      【讨论】:

        【解决方案4】:

        警告:这不能保证在标准 C 中有效(从技术上讲,浮点数不必是 IEEE754 binary32/64;它们可能是十进制实现)。但是,从 C99 开始,该标准已声明 IEEE754 样式(“IEC 60559”)浮点表示是规范性的,并在附件 F 中对其进行了定义。(实际上,您将遇到的大多数硬件已经使用 IEEE754。)

        这应该在这样的硬件上编译并且是正确的(通过一些断言来确定你正在处理 IEEE754 binary32 浮点数):

        #include <float.h>
        #include <stdint.h>
        
        // C99 specifies a "__STDC_IEC_559__" which, if defined, means you
        // can be really confident your implementation uses IEEE754 floats.
        // Unfortunately, neither GCC nor Clang seems to actually define it.
        
        // Instead, GCC defines a "__GCC_IEC_559", indicating support, and
        // glibc on Linux provides a special magic stdc-predef.h header that
        // GCC knows about and incorporates into compilation units without
        // explicit inclusion.[1]
        
        // So, long story short, if you're on Linux and use glibc and GCC,
        // great; just check for __STDC_IEC_559__.  Otherwise:
        
        _Static_asssert(FLT_RADIX == 2, "IEEE754 floats have radix 2");
        _Static_asssert(FLT_MANT_DIG == 24 && FLT_MIN_EXP == -125 &&
            FLT_MAX_EXP == 128 && FLT_HAS_SUBNORM == 1,
            "IEEE754 float characteristics");
        _Static_assert(sizeof(float) == sizeof(uint32_t), "size");
        
        float f;
        uint32_t x = 0x424ce027UL;
        
        memcpy(&f, &x, sizeof(f));
        printf("%f\n", f));
        

        它给出了输出:

        51.218899

        参考:http://lists.llvm.org/pipermail/llvm-bugs/2017-January/053186.html

        【讨论】:

        • 请记住,通过指针强制转换的类型双关语违反了 C 别名规则(有关有效类型,请参阅 C99 6.5 §6-7),即代码是非标准的...
        • 与其他答案和int 一样,这需要longfloat 的大小相同。就我个人而言,我更可能遇到long 太大(64 位 linux)的平台,而不是int 太小(大多数 16 位 CPU)的平台,但是 YMMV。我说“需要”,实际上它适用于 little-endian 64 位 linux,但在某种程度上,我将其描述为“从牙齿的皮肤”;-)
        • @Steve:我敢打赌它不适用于 PPC64。话虽如此,我会解决它。
        【解决方案5】:

        也许……

        float f = *reinterpret_cast<float*>(ptr);
        

        虽然在我的 x86 机器上,我还必须反转字符的字节顺序才能获得您想要的值。

        std::reverse(ptr, ptr + 4);
        float f = *reinterpret_cast<float*>(ptr);
        

        您可能希望使用 sizeof(float) 而不是 4 或其他方式来获取大小。您可能想要反转字节的副本,而不是原始的。不管你怎么做,这有点难看。

        edit:正如 cmets 中所指出的,这段代码是不安全的,因为它创建了两个指针,它们对同一内存进行别名,但类型不同。它可能适用于特定的编译器和程序,但不受标准的保证。

        【讨论】:

        • @JohnB:我相信这是 UB,因为别名规则。你不能像float那样随机缓冲。
        • 我认为 char* 有一个例外,它允许指向任何类型,基本上是为了做这种事情?我对这条规则的理解不正确吗? (我知道这里的 unsigned char* 可能会影响我的陈述,不确定)。
        • @JohnB:规则只有一种方式,您可以将任何对象视为一堆字符,但不能反过来。
        • 嗯,很有趣。 (而且有点烦人:))像这样使用 memcpy 是否“安全”: memcpy(&f, ptr, 4);通过测试它可以工作,并且实际上不会在我测试它的编译器上生成任何额外的代码,因此同样有效。但是标准是否可以接受?
        • 严格的别名规则是 6.5/6-7。 memcpy 是可以的,因为使用后目标对象的“有效类型”是float(它的声明类型),并且您以float 访问它,没问题。我认为这个 reinterpret_cast 从技术上讲是不行的,因为对象的“有效类型”是 char 数组,但是你以 float 的形式访问它。
        【解决方案6】:

        无符号字符 ptr[] = {0x42,0x4c,0xe0,0x27};

        float fTemp;

        uint8_t *temp2 = (uint8_t *) &fTemp;
        for (int i = 0; i < sizeof(float); i++)
            temp2[i] = ptr[3-i];
        
        std::cout<<"Data1: "<<fTemp;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-08-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-12-16
          相关资源
          最近更新 更多