C#Double和Decimal的二进制表示

82 篇文章 3 订阅

 float占用32位,double占用64位,decimal占128位。

decimal的底数是10,是十进制的。由4个整数(Int32)组成

编译器标准都遵照IEEE制定的浮点数表示法来进行float,double运算。这种结构是一种科学计数法,用符号、指数和  
   
  尾数来表示,底数定为2——即把一个浮点数表示为尾数乘以2的指数次方再添上符号。下面是具体的规格:  
   
                    符号位     阶码   尾数   长度  
  float            1              8         23       32  
  double       1            11         52       64 

decimal的底数是10,是十进制的。有4个Int32整数组成。低low、中middle、高high、【符号位和小数位数】组成的一个整数。decimal的底数是10。【注意decimal的根脚与double的根脚不同。decimal是10,double和float是2】
 Decimal转化为4个Int逻辑:将decimal去除小数点【不考虑正负号】后如 1234.5678M 整数部分是12345678。将去除小数点后的数字【12345678】转化为二进制。
       因整数部分由96位组成,二进制左侧填充0(如果不够96位),使其凑够96位。这96位二进制 每隔32位 对应低low、中middle、高high三个整数。
       第四个整数二进制格式形如:[X符号位]000 0000|000 [XXXXX就是几位小数,最多28位小数]|0000 0000|0000 0000。符号位:负数为1正数为0,【XXXXX】scale含有几位小数【小数点后有几位】,如有19位小数就是[10011]

一、下面用测试程序测试,源程序如下:

参考decimal源码:https://referencesource.microsoft.com/#mscorlib/system/decimal.cs,b39b204a56d1fbee

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

namespace AnalyzeDoubleAndDecimalDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.SetWindowSize(150, 40);
            TestDoubleToBinary();
            Console.WriteLine();
            Console.WriteLine("......下面测试Decimal转二进制数......");
            TestDecimalToBinary();
            Console.ReadLine();
        }

        /// <summary>
        /// 双精度浮点数Double转二进制示例
        /// float和double的底数是2
        /// </summary>
        static void TestDoubleToBinary()
        {
            //解析双精度double和小数Decimal的二进制表示
            //double共64位 其中符号位S占1位,指数位E占11位,尾数位M占52位
            double d = 12345678908888.0040625;
            //整数进行除基取余
            long partialInteger = 12345678908888L;
            Stack<long> stack = new Stack<long>();
            while (partialInteger != 0)
            {
                long cur = partialInteger % 2;
                stack.Push(cur);
                partialInteger = partialInteger / 2;
            }
            //整数部分的二进制字符串
            string partialIntegerBinary = string.Join("", stack);
            Console.WriteLine($"整数部分【{12345678908888}】的二进制显示:{partialIntegerBinary}");
            //小数部分
            double partialDecimal = 0.0040625;
            int scale = 32;
            int index = 0;
            Queue<int> queue = new Queue<int>();
            while (index < scale)
            {
                int cur = (int)(partialDecimal * 2);
                queue.Enqueue(cur);
                partialDecimal = partialDecimal * 2 - cur;
                if (partialDecimal == 0)
                {
                    break;
                }
                index++;
            }
            //小数部分的二进制字符串
            string partialDecimalBinary = string.Join("", queue);
            Console.WriteLine($"小数部分【{0.0040625}】的二进制显示:{partialDecimalBinary}");
            string mergeBinary = partialIntegerBinary + "." + partialDecimalBinary;
            Console.WriteLine($"合并后的二进制为:{mergeBinary}");
            //科学表示法:将小数点移动到第一位的后面
            string scienceBinary = mergeBinary.Remove(partialIntegerBinary.Length, 1).Insert(1, ".");
            //指数,最大幂级:就是2的多少次方
            int exponentNum = partialIntegerBinary.Length - 1;
            Console.WriteLine($"科学表示法为({scienceBinary})×2的【{exponentNum}】次方");
            //double的阶码,一共11位,因为指数可以为负,为了便于计算,规定都先加上1023
            int exponent = 1023 + exponentNum;
            //阶码需要凑够11位,不够的话左边填充0
            string exponentBinary = Convert.ToString(exponent, 2).PadLeft(11, '0');
            //【因科学表示法的开始一定是"1.",故尾数部分去除掉"1."】尾数部分:去除scienceBinary开始的"1.",也就是字符串从索引2开始。并凑够52位【不够的话右边填充0】
            string partialMantissa = scienceBinary.Substring(2).PadRight(52, '0');//尾数位占用52位
            //符号位:浮点数小于0为1,浮点数大于0为0
            string partialSign = (d < 0 ? "1" : "0");
            //1符号位+11阶码位+52尾数位=64位
            string total64Binary = partialSign + exponentBinary + partialMantissa;
            Console.WriteLine($"整体拼接64位二进制为:{total64Binary}");
            byte[] buffer = new byte[8];
            for (int i = 0; i < 8; i++)
            {
                buffer[i] = Convert.ToByte(total64Binary.Substring(8 * i, 8), 2);
            }
            Console.WriteLine($"double数【{d}】由8个字节组成,依次为:{string.Join(",", buffer)}");
            byte[] bufferConverter = BitConverter.GetBytes(d);
            Console.WriteLine($"【使用BitConverter】double数【{d}】由8个字节组成,依次为:{string.Join(",", bufferConverter)}");
            Console.WriteLine($"将bufferConverter数组反转后为{string.Join(",", bufferConverter.Reverse())}");
            Console.WriteLine($"将bufferConverter数组反转后 与 buffer完全一致。因本操作系统是低字节在前:【{BitConverter.IsLittleEndian}】");
        }

        /// <summary>
        /// 十进制小数Decimal转二进制示例
        /// decimal的底数是10。【注意decimal的根脚与double的根脚不同。decimal是10,double和float是2】
        /// Decimal转化为4个Int逻辑:将decimal去除小数点后如 1234.5678M 整数部分是12345678。将去除小数点后的数字【12345678】转化为二进制。
        /// 因整数部分由96位组成,二进制左侧填充0(如果不够96位),使其凑够96位。这96位二进制 每隔32位 对应低low、中middle、高high三个整数。
        /// 第四个整数二进制格式形如:[X符号位]000 0000|000 [XXXXX就是几位小数,最多28位小数]|0000 0000|0000 0000。符号位:负数为1正数为0,【XXXXX】scale含有几位小数【小数点后有几位】,如有19位小数就是[10011]
        /// </summary>
        static void TestDecimalToBinary()
        {
            /*
             The low, middle, high, and flags fields contain the representation of the Decimal value.
        The lo, mid, and hi fields contain the 96-bit integer part of the Decimal. 【96位数字】
        Bits 0-15 (the lower word) of the flags field are unused and must be zero; 
        bits 16-23 contain must contain a value between 0 and 28, indicating the power of 10 to divide the 96-bit integer part by to produce the Decimal value; 
        bits 24-30 are unused and must be zero; and finally bit 31 indicates the sign of the Decimal value, 0 meaning positive and 1 meaning negative.
        NOTE: Do not change the order in which these fields are declared. The native methods in this class rely on this particular order.        
            */
            //decimal的源码查看:https://referencesource.microsoft.com/#mscorlib/system/decimal.cs,b39b204a56d1fbee
            decimal m = 1234567890.1015625M;
            //整数进行除基取余
            long partialInteger = 1234567890L;
            Stack<long> stack = new Stack<long>();
            while (partialInteger != 0)
            {
                long cur = partialInteger % 10;
                stack.Push(cur);
                partialInteger = partialInteger / 10;
            }
            //整数部分的二进制字符串
            string partialIntegerBinary = string.Join("", stack);
            Console.WriteLine($"整数部分【不考虑正负符号】【{1234567890}】的十进制显示:{partialIntegerBinary}");
            //小数部分
            decimal partialDecimal = 0.1015625M;
            int scale = 64;
            int index = 0;
            Queue<int> queue = new Queue<int>();
            while (index < scale)
            {
                int cur = (int)(partialDecimal * 10);
                queue.Enqueue(cur);
                partialDecimal = partialDecimal * 10 - cur;
                if (partialDecimal == 0)
                {
                    break;
                }
                index++;
            }
            //小数部分的十进制字符串
            string partialDecimalBinary = string.Join("", queue);
            Console.WriteLine($"小数部分【{0.1015625}】的十进制显示:{partialDecimalBinary}");
            string mergeBinary = partialIntegerBinary + partialDecimalBinary;
            Console.WriteLine($"【去除小数点】合并后的十进制为:{mergeBinary}");
            long threeInts = (long)decimal.Parse(mergeBinary);
            //整数进行除基取余
            stack = new Stack<long>();
            while (threeInts != 0)
            {
                decimal cur = threeInts % 2;
                stack.Push((long)cur);
                threeInts = threeInts / 2;
            }
            //整数部分的二进制字符串
            string partialThreeIntegerBinary = string.Join("", stack);
            Console.WriteLine($"【高、中、低】整数部分【{mergeBinary}】的二进制显示:{partialThreeIntegerBinary}");
            //低low、中midddle、高high 共有96位
            string bits96 = partialThreeIntegerBinary.PadLeft(96, '0');
            Console.WriteLine($"拼接成96位二进制为{bits96}");
            int[] threeIntBits = new int[3];
            for (int i = 0; i < 3; i++)
            {
                threeIntBits[i] = Convert.ToInt32(bits96.Substring(32 * i, 32), 2);
            }
            Console.WriteLine($"【高high、中midddle、低low】拼接的三个整数是{string.Join(",", threeIntBits)}");
            //最后一个Int的由[X符号位,负数为1,正数为0]000 0000|000 [XXXXX就是几位小数,最多28位小数]|0000 0000|0000 0000
            int scaleNum = partialDecimalBinary.Length;//小数位数,含有几位小数
            string lastIntBinary = (m < 0 ? "1" : "0") + "0".PadRight(7, '0') + Convert.ToString(scaleNum, 2).PadLeft(8, '0') + "0".PadRight(16, '0')   ;
            Console.WriteLine($"最后一个整数的二进制表示为{lastIntBinary.Insert(24," ").Insert(16, " ").Insert(8, " ")}");
            byte[] bufferLastInt = new byte[4];
            for (int i = 0; i < 4; i++)
            {
                bufferLastInt[i] = Convert.ToByte(lastIntBinary.Substring(i * 8, 8), 2);
            }
            Console.WriteLine($"最后一个整数的数值为{Convert.ToInt32(lastIntBinary, 2)},对应的4个字节依次为{string.Join(",", bufferLastInt)}");

            Console.WriteLine($"decimal数【{m}】由4个整数组成.\n其中【低32位整数low】=【{threeIntBits[2]}】,【中32位整数middle】=【{threeIntBits[1]}】,【高32位整数high】=【{threeIntBits[0]}】,【符号和小数位数组成的整数flagAndScale】=【{Convert.ToInt32(lastIntBinary, 2)}】");
            int[] bufferConverterInts = decimal.GetBits(m);
            Console.WriteLine($"【使用GetBits】decimal数【{m}】由4个整数组成,依次为:{string.Join(",", bufferConverterInts)}");
            byte[] bufferConverter = bufferConverterInts.SelectMany(numInt => BitConverter.GetBytes(numInt)).ToArray();
            Console.WriteLine($"【使用BitConverter】decimal数【{m}】由16个字节组成,依次为:{string.Join(",", bufferConverter)}");
        }
    }
}
二、程序运行如图:

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

斯内科

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值