C#关于64位双精度浮点数Double(DReal)一步步按位Bit进行解析

有一篇我们是获取float如何转化为字节的过程,这次我们测试double是如何转化为64位二进制的。

C#关于32位浮点数Float(Real)一步步按位Bit进行解析

基本说明

浮点数的32位N=1符号位(Sign)+8指数位(Exponent)+23尾数部分(Mantissa) 
符号位(Sign) : 0代表正,1代表为负【占1位】

指数位(Exponent)::用于存储科学计数法中的指数数据,并且采用移位存储【占8位】,

指数位的值:127 +(整数部分转化为二进制的字符串长度-1)

尾数部分(Mantissa):尾数部分【占23位】,因此小数点后最多精确到23/4=6位 。

双精度数的64位N=1符号位(Sign)+11指数位(Exponent)+52尾数部分(Mantissa) 
符号位(Sign) : 0代表正,1代表为负【占1位】

指数位(Exponent)::用于存储科学计数法中的指数数据,并且采用移位存储【占11位】,

指数位的值:1023 +(整数部分转化为二进制的字符串长度-1)

尾数部分(Mantissa):尾数部分【占52位】,因此小数点后最多精确到52/4=13位 。

表格如下:

类型 总位数 符号位(Sign) 指数位(Exponent) 尾数位(Mantissa) 
Float 32 1 8 23
Double 64 1 11 52

          

十进制小数的二进制表示:

【法则--整数部分:除基取余,逆序拼接。小数部分:乘基取整,顺序拼接】            

整数部分:除以2,取出余数,商继续除以2,直到得到0为止,将取出的余数逆序。可以使用栈Stack            

小数部分:乘以2,然后取出整数部分,将剩下的小数部分继续乘以2,然后再取整数部分,一直取到小数部分为零为止。如果永远不为零,则按要求保留足够位数的小数,最后一位做0舍1入。将取出的整数顺序排列。可以使用队列Queue

指数位是 2的10次方 然后 再减去一 为基础,然后加上小数点移位次数,即:1023+moveCount 

符号位 使用 Math.Sign(number) 如果小于0【负数】,则为1,否则为0

整数部分:Math.Truncate(number)

整个测试代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AnalyseDoubleDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //double共使用64位,符号位占用1位+指数位占用11位+尾数位占用52位=64位
            double d = -12345678988.15625D;
            AnalyseDoubleToBinary(d);
            Console.WriteLine();
            d = 123456.789D;
            AnalyseDoubleToBinary(d);
            Console.ReadLine();
        }

        /// <summary>
        /// 将双精度浮点数转化为64位二进制字符串
        /// </summary>
        /// <param name="d"></param>
        static void AnalyseDoubleToBinary(double d) 
        {
            //整数部分
            double integerPart = Math.Truncate(d);
            //小数部分
            double fractionalPart = Math.Abs(d - integerPart);
            Console.WriteLine($"整数部分【{integerPart}】,小数部分【{fractionalPart}】");
            string sign = Math.Sign(d) < 0 ? "1" : "0";//符号位:负数为1,其他为0

            //只用考虑正数对应的二进制
            long longPartNumber = Math.Abs((long)integerPart);
            Stack<long> stack = new Stack<long>();
            while (longPartNumber != 0)
            {
                stack.Push(longPartNumber % 2);
                longPartNumber = longPartNumber / 2;
            }
            Console.WriteLine($"正整数部分的二进制【{string.Join("", stack)}】");

            int scale = 60;
            int index = 0;
            Queue<int> queue = new Queue<int>();
            while (index < scale)
            {
                int cur = (int)(fractionalPart * 2);
                queue.Enqueue(cur);
                fractionalPart = fractionalPart * 2 - cur;
                if (fractionalPart == 0)
                {
                    break;
                }
                index++;
            }
            Console.WriteLine($"小数部分的二进制【{string.Join("", queue)}】");
            string binaryDisplay = string.Join("", stack) + "." + string.Join("", queue);
            Console.WriteLine($"{d}对应的二进制为{binaryDisplay}");
            int dotIndex = binaryDisplay.IndexOf('.');
            //移除小数点后将小数点插入索引1的位置【即:小数点移动到索引1的位置】             
            string scienceDisplay = binaryDisplay.Remove(dotIndex, 1).Insert(1, ".");
            Console.WriteLine($"小数{d}对应的二进制科学计数为{scienceDisplay}×(2的{dotIndex - 1}次方)");
            string exponent = Convert.ToString(1023 + (dotIndex - 1), 2).PadLeft(11, '0');//指数位占用11位
            //尾数部分:去除scienceDisplay开始的"1.",也就是字符串从索引2开始。并凑够52位
            string mantissa = scienceDisplay.Substring(2).PadRight(52, '0');//尾数位占用52位
            //只截取52位尾数部分
            if (mantissa.Length > 52) 
            {
                mantissa = mantissa.Substring(0, 52);
            }
            string joinBits = sign + exponent + mantissa;//符号位占用1位+指数位占用11位+尾数位占用52位=64位
            Console.WriteLine($"小数{d}对应的64位二进制为【{joinBits}】");

            byte[] bufferJoin = new byte[8];
            for (int i = 0; i < 8; i++)
            {
                bufferJoin[i] = Convert.ToByte(joinBits.Substring(8 * i, 8), 2);
            }
            Console.WriteLine("重新拼接形成的32位浮点数,对应的8个字节为:");
            Console.WriteLine(string.Join(",", bufferJoin));
            Console.WriteLine("打印双精度浮点数对应的8个字节为:");
            Console.WriteLine(string.Join(",", BitConverter.GetBytes(d)));
            Console.WriteLine("反转顺序对应的8个字节为:");
            Console.WriteLine(string.Join(",", BitConverter.GetBytes(d).Reverse()));
            //字节反转后 和 对应的8个字节数组完全一致,因C#是低字节在前的
        }
    }
}

程序运行如图:

【C#数据在内存中是从低字节开始存储的】


 

猜你喜欢

转载自blog.csdn.net/ylq1045/article/details/120865767
今日推荐