从IEEE754浮点格式读取字节的正确方法

时间:2018-07-31 07:33:31

标签: c floating-point byte ieee-754

我有一个要求,我需要读取单精度IEEE754浮点数表示形式的4个原始字节,以便按原样在串行端口上进行发送。我只是想问一下以下提取字节的正确方法是什么?

1。)创建一个联合,例如:

typedef union {
  float f;
  uint8_t bytes[4];
  struct {
    uint32_t mantissa : 23;
    uint32_t exponent : 8;
    uint32_t sign : 1;
  };
} FloatingPointIEEE754_t ;

,然后在写入浮点变量bytes[]之后只读取f数组吗?

2。)或者,通过使uint32_t类型的指针指向float变量的函数来提取字节,然后通过屏蔽来提取字节

uint32_t extractBitsFloat(float numToExtFrom, uint8_t numOfBits, uint8_t bitPosStartLSB){
  uint32_t *p = &numToExtFrom;
  /* validate the inputs */
  if ((numOfBits > 32) || (bitPosStartLSB > 31)) return NULL;
  /* build the mask */
  uint32_t mask = ((1 << numOfBits) - 1) << bitPosStartLSB;
  return ((*p & mask) >> bitPosStartLSB);
}

进行呼叫的方式如下:

valF = -4.235;
byte0 = extractBitsFloat(valF, 8, 0);
byte1 = extractBitsFloat(valF, 8, 8);
byte2 = extractBitsFloat(valF, 8, 16);
byte3 = extractBitsFloat(valF, 8, 24);

如果您认为上述两种方法都错了,请给我建议正确的方法!

3 个答案:

答案 0 :(得分:3)

首先,我假设您专门针对float实际上是在IEEE754 中表示的{strong> 的平台进行编码。一般来说,您不能将其视为理所当然的,因此您的代码无法移植到所有平台。

然后,union方法是正确的方法。但是不要添加该位域成员!无法保证位的排列方式,因此您可能会访问错误的位。只需这样做:

typedef union {
  float f;
  uint8_t bytes[4];
} FloatingPointIEEE754;

此外,请勿在您自己的类型中添加后缀_t。在POSIX系统上,这是为实现保留的,因此最好始终避免这种情况。

除了使用union,还可以通过char指针访问字节:

unsigned char *rep = (unsigned char *)&f;
// access rep[0] to rep[3]

请注意,在两种情况下,您都在访问内存中的表示形式,这意味着您必须注意计算机的endianness


您的第二个选项不正确,它违反了 strict别名规则。简而言之,不允许您通过不具有兼容类型的指针访问对象,char指针是访问表示形式的显式异常。确切的规则写在{11},这是C11标准的最新草案。

答案 1 :(得分:2)

您可以这样做:

unsigned char *p = (unsigned char *)&the_float;

,然后从p指向的位置读取4个字节(例如p[0]p[1]等)。最佳的“读取4个字节”代码取决于串行端口函数以何种形式接收数据。

答案 2 :(得分:1)

如果您不关心字节序,只需为指向浮点地址的字符指针添加别名。该标准明确允许使用字符指针来访问任何类型的表示形式的字节。如果您需要特定的字节序来在串行端口上发送字节,则可以在发送之前进行测试:

  1. 简单方法,只需使用本地字节序:

    float f;
    ...
    char * bytes = &f;       // bytes point the the beginning of a char array of size sizeof(f)
    
  2. 自动测试字节序,并使用大字节序(AKA网络顺序)。 struct只是一个返回数组并具有线程安全代码的技巧。

    struct float_bytes {
        char bytes[sizeof(float)];
    };
    struct float_bytes(float f) {
        float end = 1.;
        float_bytes resul;
        char *src = (char *) &f;
        if (*end == 0) {                // end is 0 on a little endian platform, else 0x3f
            int i = sizeof(f) {         // little endian: reverse the bytes
            while (i > 0) {
                resul.bytes[--i] = src++;
            }
        }
        else {                          // already in big endian order, just memcpy
            memcpy(&(resul.bytes), &f, sizeof(f));
        }
        return resul;
    }
    

    当心:仅当浮点为IEEE754单点时,字节序测试才有意义。