8位浮点数的表示法

32位浮点数的计算机内部表示是由IEEE754标准定义的,参考文献相当多。
在这里插入图片描述
简单来说,是由1位符号位、8位指数位(阶码)和23位小数部分共同定义的。如果你想看看任何一个浮点数的4个字节的二进制表示细节,可以在这个网站里进行练习。

https://www.h-schmidt.net/FloatConverter/IEEE754.html

IEEE754规范中只定义了32位(float)和64位(double)浮点的表示法,好像还有扩展的16位、128位的浮点数表示法,但用的最多的仍是32位、64位浮点。

IEEE754规范实际上还是挺复杂的,小数部分有隐含1.0的情况(规格化的小数),也有非规格化的小数,阶码要加偏移量,还要表示无穷大,NaN这些特殊的值,还要让数字0的内部表示全为0。

8位浮点数表示法,并没有相关规范,有一个MiniFloat,但与我们教科书的不太一样。

这里是一种简化的8位浮点数定义:
在这里插入图片描述

1)这里的符号位是最高位:0为正,1为负
2)指数位,阶码,3位,为了表示-4到3的范围,需要减去4,即000表示-4,111表示3,注意与IEEE754规范不同
3)小数位,4位:按IEEE754规范,隐含着1.0

为了试验这些浮点数,我写了一个C#程序:

/// <summary>
/// 8位浮点数表示
/// +----+-----------+----------------+
/// + b7 |  b6 b5 b4 | b3  b2  b1  b0 |
/// +sign|    exp    |    mantissa    |
/// +----+-----------+----------------+
/// 符号位:0为正,1为负
/// 指数位,阶码,3位:000表示-4,111表示3
/// 小数位,4位:按IEEE754规范,隐含着1.0
/// </summary>
public struct MiniFloat
{
    public readonly static double DEFAULT_MANTISSA = 1.0;
    public int sign; // 符号位,0为正,1为负
    public int e; // [-4, 3]
    public int m; // [0, 15]

    public MiniFloat(int s, int e, int m)
    {
        this.sign = s;
        this.e = e;
        this.m = m;
    }

    public MiniFloat(int binary)
    {
        this.sign = (binary & 0x80) >> 7;
        this.e = ((binary & 0x70) >> 4) - 4; // 3位阶码,表示范围为0到7,减去4后,表示范围为-4到3
        this.m = binary & 0x0F; // 后4位
    }

    public double GetValue()
    {
        return (sign == 0 ? 1 : -1) * CompMantissa(m) * Math.Pow(2, e);
    }

    public override string ToString()
    {
        string s = string.Format("值:{0},指数部分:{1},小数部分:{2},二进制表示:{3}", GetValue(), e, m, BinaryString());
        return s;
    }

    public string GetDetails()
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine("数值:" + GetValue());
        sb.AppendLine("二进制表示:" + BinaryString());
        sb.AppendLine("小数部分(二进制):" + (int)DEFAULT_MANTISSA
            + "." + Convert.ToString(m, 2).PadLeft(4, '0'));
        double manti = MiniFloat.CompMantissa(this.m);
        sb.AppendLine("小数部分(十进制):" + manti);
        sb.AppendLine("阶码:" + this.e);
        sb.AppendLine(GetValue() + " = " + manti + " * (2 ^ " + this.e + ")");
        return sb.ToString();
    }

    public string BinaryString()
    {
        return Convert.ToString((sign << 7 | ((e + 4) & 0x7) << 4) | m, 2).PadLeft(8, '0');
    }

    static public double CompMantissa(int bits)
    {
        double[] v = { 0.5, 0.25, 0.125, 0.0625 };
        double mantissa = DEFAULT_MANTISSA; // IEEE754规定小数部分都是1.0开始
        for (int i = 0; i < v.Length; i++)
        {
            if ((bits & 0x08) != 0)
            {
                mantissa += v[i];
            }
            bits = bits << 1;
        }
        return mantissa;
    }
}

上面是一个结构体。主程序要试验几个浮点数的8位表达细节。

static void Main(string[] args)
{
    double[] arr = { 6.25, 7, 2.5, 0.1875, 0.1, 0.0 };
    foreach (double f in arr)
    {
        MiniFloat a = TryMiniFloat(f);
        if (Math.Abs(a.GetValue() - f) > 1.0e-8)
        {
            Console.WriteLine("!!!!!!!!!!不能准确表达 " + f);
        }
        Console.WriteLine(a.GetDetails());
    }
    Console.ReadLine();
}

static MiniFloat TryMiniFloat(double f)
{
    double eps = 999.0;
    MiniFloat mini = new MiniFloat { };
    for (int i = 0; i < 256; i++)
    {
        MiniFloat temp = new MiniFloat(i);
        double approx = temp.GetValue();
        if (Math.Abs(approx - f) < eps)
        {
            eps = Math.Abs(approx - f);
            mini = temp;
        }
    }
    return mini;
}

可以发现,这种表示方法,不能精确地表示0.1。
最接近0.1的数是:0.1015625
二进制表示:00001010
小数部分(二进制):1.1010
小数部分(十进制):1.625
阶码:-4
0.1015625 = 1.625 * (2 ^ -4)

提醒注意,这个表示法只是为了简要说明浮点数的表示法,很多细节与IEEE754不同:
1)不能准确表示0,IEEE754规定0.0一定是4个字节的全零
2)阶码的偏移量也不对,按IEEE754,偏移量应该为3
3)当阶码不是000,也不是111的时候,这个时候尾数应该是规格化的数,即隐含着1.0
4)当阶码为000时,尾数为非规格化的数
5)当阶码为111时,IEEE规定为NaN特殊值

这些细节我没空深究下去了,欢迎大家讨论。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

申龙斌

撸代码来深夜,来杯咖啡钱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值