0. 简介

之前我们在《经典文献阅读之—R-PCC(基于距离图像的点云压缩方法)》中提到了,我们可以通过一些算法层面来完成数据的压缩,而其实更简单或者说更直接的方法就是使用half这种形式来完成数据压缩。

1. half和float

Half是用16位表示浮点数的一种数据类型,在IEEE 754中也有规定,这种数据类型在深度学习系统中的应用比较广泛。但是在当前主流cpu上,不支持half类型数据的计算和输出,所以需要half和float两个数据类型之间的转换。

图1是16位浮点表示的标准,其中包括了1个符号位,5个指数位和10个尾数位。对于正常的数值,其结果如下表示。

图2是32位浮点数表示的标准,其中包括1个符号位,8个指数位和23个尾数位。对于正常的数值,其结果如下表示。

所以对于half和float之间的转换,除了不同部分的移位之外,还要做注意指数的基数之间的差别(15和127)。要把half类型转换为float类型,主要进行以下几步操作。

  • 符号位左移16位。
  • 指数部分加112(127与15之间的差距),左移13位(右对齐)。
  • 尾数部分左移13位(左对齐)。

2. half与float参考代码

下面是对应的参考代码:

typedef unsigned short ushort;//占用2个字节
typedef unsigned int uint;    //占用4个字节

uint as_uint(const float x) {
    return *(uint*)&x;
}
float as_float(const uint x) {
    return *(float*)&x;
}

float half_to_float(const ushort x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits
    const uint e = (x&0x7C00)>>10; // exponent
    const uint m = (x&0x03FF)<<13; // mantissa
    const uint v = as_uint((float)m)>>23; // evil log2 bit hack to count leading zeros in denormalized format
    return as_float((x&0x8000)<<16 | (e!=0)*((e+112)<<23|m) | ((e==0)&(m!=0))*((v-37)<<23|((m<<(150-v))&0x007FE000))); // sign : normalized : denormalized
}
ushort float_to_half(const float x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits
    const uint b = as_uint(x)+0x00001000; // round-to-nearest-even: add last bit after truncated mantissa
    const uint e = (b&0x7F800000)>>23; // exponent
    const uint m = b&0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
    return (b&0x80000000)>>16 | (e>112)*((((e-112)<<10)&0x7C00)|m>>13) | ((e<113)&(e>101))*((((0x007FF000+m)>>(125-e))+1)>>1) | (e>143)*0x7FFF; // sign : normalized : denormalized : saturate
}


//下面的demo中,yolov5_outputs[0].buf是void *类型的,void *类型不能++,因此先转换成ushort*类型。

    float *data0 = (float*)malloc(4 * output_attrs[0].n_elems);
    float *data1 = (float*)malloc(4 * output_attrs[1].n_elems);
    float *data2 = (float*)malloc(4 * output_attrs[2].n_elems);
    unsigned short *temp0 = (ushort*)yolov5_outputs[0].buf;
    unsigned short *temp1 = (ushort*)yolov5_outputs[1].buf;
    unsigned short *temp2 = (ushort*)yolov5_outputs[2].buf;

    for(int i=0; i < output_attrs[0].n_elems;i++)
    {
        data0[i] = half_to_float(temp0[i]);
    }
    for(int i=0; i < output_attrs[1].n_elems;i++)
    {
       data1[i] = half_to_float(temp1[i]);
    }
    for(int i=0; i < output_attrs[2].n_elems;i++)
    {
       data2[i] = half_to_float(temp2[i]);

3. float与uint16

求在串口通信时常常会被用到,串口只能以字符型(char)进行通信
atof():将字符串转换为双精度浮点型值。
atoi():将字符串转换为整型值。
浮点转uint16函数

uint16_t float_cov_uint16(float value)
{

    const Fp32 f32infty = { 255U << 23 };
    const Fp32 f16infty = { 31U << 23 };
    const Fp32 magic = { 15U << 23 };
    const uint32_t sign_mask = 0x80000000U;
    const uint32_t round_mask = ~0xFFFU;

    Fp32 in;
    uint16_t out;

    in.f = value;

    uint32_t sign = in.u & sign_mask;
    in.u ^= sign;

    if (in.u >= f32infty.u) /* Inf or NaN (all exponent bits set) */
    {
        /* NaN->sNaN and Inf->Inf */
        out = (in.u > f32infty.u) ? 0x7FFFU : 0x7C00U;
    }
    else /* (De)normalized number or zero */
    {
        in.u &= round_mask;
        in.f *= magic.f;
        in.u -= round_mask;
        if (in.u > f16infty.u)
        {
            in.u = f16infty.u; /* Clamp to signed infinity if overflowed */
        }

        out = uint16_t(in.u >> 13); /* Take the bits! */
    }

    out = uint16_t(out | (sign >> 16));

    return out;
}

uint16转float函数

float uint6_cov_float(uint16_t value)
{
    const Fp32 magic = { (254U - 15U) << 23 };
    const Fp32 was_infnan = { (127U + 16U) << 23 };
    Fp32 out;

    out.u = (value & 0x7FFFU) << 13;   /* exponent/mantissa bits */
    out.f *= magic.f;                  /* exponent adjust */
    if (out.f >= was_infnan.f)         /* make sure Inf/NaN survive */
    {
        out.u |= 255U << 23;
    }
    out.u |= (value & 0x8000U) << 16;  /* sign bit */

    return out.f;
}

4. PCL简单的转换代码

对于使用PCL而言,我们可以采用另一种形式

#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <vector>
#include <iostream>

typedef pcl::PointCloud<pcl::PointXYZ> PointCloudXYZ;

PointCloudXYZ get_pcl(std::vector<unsigned short> points)
{
    PointCloudXYZ cloud;
    cloud.width = points.size();
    cloud.height = 1;
    cloud.is_dense = false;
    cloud.points.resize(cloud.width * cloud.height);

    for (int i = 0; i < points.size()-3; i+=3) 
    {
        cloud.points[i].x = static_cast<float>(points[i]);
        cloud.points[i].y = static_cast<float>(points[i+1]);
        cloud.points[i].z = static_cast<float>(points[i+2]);
    }

    return cloud;
}

std::vector<unsigned short> get_point(PointCloudXYZ pcl) {
  std::vector<unsigned short> result;
  for (const auto &point : pcl.points) {
    // convert x, y, z from float to unsigned short
    unsigned short x = static_cast<unsigned short>(point.x);
    unsigned short y = static_cast<unsigned short>(point.y);
    unsigned short z = static_cast<unsigned short>(point.z);
    // add to result vector
    result.push_back(x);
    result.push_back(y);
    result.push_back(z);
  }
  return result;
}

int main(int argc, char** argv)
{
    std::vector<unsigned short> points = {1, 2, 3, 4, 5};
    PointCloudXYZ cloud = get_pcl(points);
    points = get_point(cloud);
    // Output the point cloud to verify the conversion
    std::cout << "Point cloud data: " << std::endl;
    for (int i = 0; i < cloud.points.size(); i++) 
        std::cout << cloud.points[i].x << " " << cloud.points[i].y << " " << cloud.points[i].z << std::endl;
    return 0;
}

5. 参考链接

https://blog.csdn.net/softee/article/details/79494335

https://blog.csdn.net/qq_39016531/article/details/107411030