C#半精度浮点数的实现

C#只提供了单精度类型的float,但有时候单精度也显得有点浪费,毕竟一个单精度也需要四个字节。有些语言已经开始支持半精度了,但C#目前还不支持,于是自己写了个float转单精度的函数,此函数将float类型的数据转换成byte[],返回的数组中其实只有两个元素,因为半精度的大小就是两字节。当得到了转换之后的byte[]后,可以把它存到文件里,等需要分析数据的时候,再读取出来,用转换函数转成float类型。采用这种方法,可以节省一半的存储空间,当然也得接受数据精度受损的风险。

从float转换成byte[ ]

 static byte[] FloatToHalf(float f)
        {          
            Byte[] bytes = BitConverter.GetBytes(f);
            Byte sign = 0x80;
            SByte exp;
            byte[] myByte = new byte[2];//返回的数组
            ushort result;

            ushort m;
            sign = (Byte)(bytes[3] & sign);//求符号位
            //求指数位
            exp = (SByte)(bytes[3] << 1);
            exp += (SByte)(bytes[2] >> 7);
            exp -= 127;
            exp += (SByte)((1<<(expSize -1)) -1);
            if (exp < 0)//下溢出
                exp = 0;
            //求尾数
            m = (ushort)(bytes[2] & 0x7f);
            m = (ushort)(m << (mSize - 7));
            m += (ushort)(bytes[1] >> (15-mSize));
            if (((bytes[1] >> (15 - mSize - 1)) & 1) == 1)//若被移除的最高位是1,则产生进位。
                m += 1;
            if (m >= (ushort)Math.Pow(2, mSize))//若进位后发生尾数溢出,则取消进位
                m -= 1;

            result = sign;
            result = (ushort)(result << 8);//把符号位移动到最高位上
            //装载指数位
            short temp1 = exp;
            temp1 = (short)(temp1 << (15-expSize));
            result += (ushort)temp1;
            result += m;//装载尾数
            myByte[0] = (byte)result;
            myByte[1] = (byte)(result >> 8);
            return myByte;
        }

从byte[ ]转换成float的函数

  static float HalfToFloat(byte[] myByte)
        {
            ushort h = myByte[1];
            h = (ushort)(h << 8);
            h += myByte[0];
            int sign = 1;
            int exp;
            uint m;
            float result;
            double temp = 0;
            if ((h >> 15) == 1)
                sign = -1;
            exp = h & 0x00007fff;
            exp = (exp >> (mSize));//提取指数位
            exp -= ((1 << (expSize - 1)) - 1);
            m = (uint)(h << (expSize + 1)) >> (expSize + 1);
            for(int i = 0;i < mSize; i++)
            {
                if((m & 1) == 1)
                temp += Math.Pow(2, i - mSize);
                m = m >> 1;
            }
            temp += 1;
            temp *= Math.Pow(2, exp);
            result = (float)temp;
            return result * sign;
        }

上面的这两个函数中,需要用到两个全局变量

        static int expSize = 6;//指数的位数
        static int mSize = 15-expSize;//尾数的位数

学习过IEEE 754标准的都知道,以float类型为例,浮点数在计算机的存储中是被分为三部分的,符号位占1个比特,阶码占8个比特,尾数占23个比特。expSize是用来设定阶码(也就是指数)有几个比特,mSize用来设定尾数有几个比特。建议大家在设定的时候,expSize取4~6之间的整数。IEEE 754标准给出的阶码位数是5位。
有问题欢迎留言讨论。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值