C#中32位浮点数Float(Real)一步步按位Bit进行分析

 更新时间:2022年8月12日 15:50  点击:422 作者:斯内科

我们都知道单精度浮点数(Single,float,Real)由32位0或1组成,它具体是如何来的。

浮点数的32位N=1符号位(Sign)+8指数位(Exponent)+23尾数部分(Mantissa)

  • 符号位(Sign) : 0代表正,1代表为负【占1位】
  • 指数位(Exponent)::用于存储科学计数法中的指数数据,并且采用移位存储【占8位】
  • 尾数部分(Mantissa):尾数部分【占23位】
  • 单精度float:N共32位,其中S占1位,E占8位,M占23位。因此小数点后最多精确到23/4=6位 。

C#代码示例如下

using System;
using System.Collections.Generic;
using System.Linq;
namespace ConverterAndPrecisionDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //参考博客:https://blog.csdn.net/zhengyanan815/article/details/78550073
            //小数点后面:4个位占用一个数字【十进制9就是1001】
            //符号位(Sign) : 0代表正,1代表为负【占1位】
            //指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储【占8位】
            //尾数部分(Mantissa):尾数部分【占23位】
            //单精度float:N共32位,其中S占1位,E占8位,M占23位。因此小数点后最多精确到23/4=6位 
            //双精度double:N共32位,其中S占1位,E占11位,M占52位。因此小数点后最多精确到52/4=13位 
            //十进制小数的二进制表示:【法则--整数部分:除基取余,逆序拼接。小数部分:乘基取整,顺序拼接】
            //整数部分:除以2,取出余数,商继续除以2,直到得到0为止,将取出的余数逆序。可以使用栈Stack
            //小数部分:乘以2,然后取出整数部分,将剩下的小数部分继续乘以2,然后再取整数部分,一直取到小数部分为零为止。如果永远不为零,则按要求保留足够位数的小数,最后一位做0舍1入。将取出的整数顺序排列。可以使用队列Queue
            float f = 123456.8125F;
            byte[] buffer = BitConverter.GetBytes(f);
            Console.WriteLine("打印浮点数对应的4个字节:");
            Console.WriteLine(string.Join(",", buffer));
            Console.WriteLine($"【使用函数】{123456}对应的二进制:{ Convert.ToString(123456, 2)}");
            int num = 123456;
            Stack<int> stack = new Stack<int>();
            while (num != 0)
            {
                int cur = num % 2;
                stack.Push(cur);
                num = num / 2;
            }
            Console.WriteLine($"【使用堆栈】{123456}对应的二进制:{ string.Join("", stack)}");
            int scale = 10;
            int index = 0;
            double d = 0.8125;
            Queue<int> queue = new Queue<int>();
            while (index < scale)
            {
                int cur = (int)(d * 2);
                queue.Enqueue(cur);
                d = d * 2 - cur;
                if (d == 0)
                {
                    break;
                }
                index++;
            }
            Console.WriteLine($"{0.8125}对应的二进制:{ string.Join("", queue)}");
            string binaryDisplay = string.Join("", stack) + "." + string.Join("", queue);
            Console.WriteLine($"{123456.8125}对应的二进制为{binaryDisplay}");
            int dotIndex = binaryDisplay.IndexOf('.');
            //移除小数点后将小数点插入索引1的位置【即:小数点移动到索引1的位置】
            string scienceDisplay = binaryDisplay.Remove(dotIndex, 1).Insert(1, ".");
            Console.WriteLine($"小数{123456.8125}对应的二进制科学计数为{scienceDisplay}×(2的{dotIndex - 1}次方)");
            string sign = (f > 0 ? "0" : "1");//符号位占用1位
            Console.WriteLine($"符号位S:正数为0,负数为1。符号位是:{sign}");
            string exponent = Convert.ToString(127 + (dotIndex - 1), 2).PadLeft(8, '0');//指数位占用8位
            Console.WriteLine($"指数位E:123456最高位为2的{dotIndex - 1}次方,指数为{dotIndex - 1},因此指数位E的十进制值为【127+{dotIndex - 1}={127 + dotIndex - 1}】");
            //尾数部分:去除scienceDisplay开始的"1.",也就是字符串从索引2开始。并凑够23位
            string mantissa = scienceDisplay.Substring(2).PadRight(23, '0');//尾数位占用23位
            Console.WriteLine($"尾数位M:尾数部分M需要凑够23位。为【{mantissa}】");
            string joinBits = sign + exponent + mantissa;//符号位占用1位+指数位占用8位+尾数位占用23位=32位
            byte[] bufferJoin = new byte[4];
            for (int i = 0; i < 4; i++)
            {
                bufferJoin[i] = Convert.ToByte(joinBits.Substring(8 * i, 8), 2);
            }
            Console.WriteLine("重新拼接形成的32位浮点数,对应的4个字节为:");
            Console.WriteLine(string.Join(",", bufferJoin));
            byte[] reverseBuffer = bufferJoin.Reverse().ToArray();
            Console.WriteLine("反转数组bufferJoin的顺序:重新打印我们会发现与浮点数原始的字节完全一致。注意:C#是低字节在前");
            Console.WriteLine(string.Join(",", reverseBuffer));
            Console.ReadLine();
        }
    }
}

程序运行结果

关于32位浮点数的一些理解

1、定点的缺点

对于一个系统可能出现一些特别大的数和特别小的数,如果用定点表示就会很僵硬,位数一定就不能同时表达特别大的数和特别小的数。

2、对于定点123.625

用科学计数法的方式可以写成1.23625*10^2,也可以写成12.625*10^1或1.111011101*2^6。。。。。为了规范,IEEE就规定了32位浮点的格式如下

https://img2018.cnblogs.com/blog/1070689/201903/1070689-20190310165139947-1347641585.png

3、翻译一下

(1)最高位是符号位,“0”代表正,“1”代表负。

(2)接下来的8位是指数位,8位可表示整数的范围是0-255,考虑指数可以是负的,IEEE规定在上面的范围减去127,并将-127(全0)和128(全1)用做特殊值处理,所以指数的位的范围是(-127,128)。

(3)最低的23位是小数位(尾数位),正常是可以表示23位的范围,但是IEEE规定小数点左侧必须为1,右侧位数不够补0。这样可以就可以省略1,可以用23位来表示24位。

eg. 1.111011101*2^6中,小数位是111011101+补14个0

4、定点转浮点实例:123.625用32位浮点表示

科学计数法=1.111011101*26(整数部分:123=01111011b,小数部分:0.625=0.101b,整数部分除2取余,倒序排列,高位补零;小数部分乘2取整,顺序排列)

符号位:0

指数位:6+127=10000101

小数位:11101110100000000000000

即:01000010111101110100000000000000=0x42F74000

5、验证

6、浮点转定点实例

42F74000=01000010111101110100000000000000,拆分为符号位、指数位、小数位。

(1)符号位:0

(2)指数位:10000101=133,实际指数=133-127=6

(3)小数位:11101110100000000000000去掉后面的0、前面补1为1. 111011101

即科学计数法表示为1. 111011101*26=(2^0+2^-1+2^-2+2^-3+2^-5+2^-6+2^-7+2^-9)*2^6=123.625。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持猪先飞。 

原文出处:https://blog.csdn.net/ylq1045/article/details/108025459

[!--infotagslink--]

相关文章

  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • Python astype(np.float)函数使用方法解析

    这篇文章主要介绍了Python astype(np.float)函数使用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-06-08
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25