c# folat, double, decimal分析

c#中,在小数后面加f表示float,d表示double,m表示decimal,不加默认是double

var a = 1.0f; //float
var b = 1.0d; // double
var c = 1.0m; // decimal

一直没搞懂为什么需要这么多类型,下面看一下它们能表示的范围以及内部存储,其中float和double属于浮点型,decimal是MS推出的另一种类型

范围和精度

  字节数 范围 小数点
float 4 -2128 ~ +2128,也即-3.40E38 ~ +3.40E38 223 = 8388608,也就是7
double 8 -21024 ~ +21024,也即-1.79E308 ~ +1.79E308 252 = 4503599627370496也就是16
decimal 16  -(296 - 1) to 296 - 1 28

内存存储

  • float(32位):
    float的指数部分有8bit(2^8),由于是有符号型,所以得到对应的指数范围-128~128。由于float的指数部分对应的指数范围为-128~128,所以取值范围为: 
    -2128到2128,约等于-3.4E38 — +3.4E38 

    浮点数的计算有一篇文章写的非常好,写出了浮点型的内部存储详细规则以及为什么会有精度损失

    https://blog.csdn.net/u011305680/article/details/80264508

c# folat, double, decimal分析

  • double(64位):
    double的指数部分有11bit(2^11),由于是有符号型,所以得到对应的指数范围-1024~1024。 

c# folat, double, decimal分析

  • decimal(128位):

decimal 内部使用 4 个 32-bit 的 System.Int32 来存储,占用 128 bits = 16 bytes。这 128 bits 分配如下:

96 bits 表示从 0 至 296 - 1 的整数,分布在 3 个 32-bit 的 System.Int32 中。
剩下的 1 个 32-bit 的 System.Int32 包括符号位和比例因子。
第 31 bit 是符号位,0 表示正数,1 表示负数。
第 16 至 23 bit 表示比例因子,必须包含一个 0 至 28 之间的指数,指示 10 的幂,即小数点的位置,也就是小数点右边有几位数字。
其实表示 0 至 28 之间的指数只需 5 bits 就够了,而上面的第 16 至 23 bit 共 8 bits = 1 byte。也就是说剩下的 3 bits (第 21 至 23 bit) 一定是零。
其余 bits (0 - 15 bit 和 24 - 30 bit)不被使用,必须为零。

比较

由上面2个表可以看出来,浮点型和decimal是完全不同的存储方式

对于浮点型,

  首先我们要搞清楚下面两个问题:

(1) 十进制整数如何转化为二进制数

      算法很简单。举个例子,11表示成二进制数:

                11/2=5 余   1

                  5/2=2   余   1

                  2/2=1   余   0

                  1/2=0   余   1

                     0结束         
  11二进制表示为(从下往上):1011

 (2) 十进制小数如何转化为二进制数

      算法是乘以2直到没有了小数为止。举个例子,0.9表示成二进制数

                0.9*2=1.8   取整数部分 1

                0.8(1.8的小数部分)*2=1.6    取整数部分 1

                0.6*2=1.2   取整数部分 1

                0.2*2=0.4   取整数部分 0

                0.4*2=0.8   取整数部分 0

                0.8*2=1.6 取整数部分 1

                0.6*2=1.2   取整数部分 0

                .........   
    0.9二进制表示为(从上往下): 1100100100100......

上面的计算过程循环了,也就是说*2永远不可能消灭小数部分,这样算法将无限下去。很显然,小数的二进制表示有时是不可能精确的 。其实道理很简单,十进制系统中能不能准确表示出1/3呢?同样二进制系统也无法准确表示1/10。这也就解释了为什么浮点型减法出现了”减不尽”的精度丢失问题。

 

而对于decimal,整数部分和小数部分都放在后面12个字节内,不存在精度丢失的问题,小数点后面位数保存在另外4个字节的16-23个bit,只要不超出28,就不会有精度损失。下面的代码由字符串转换成decimal,然后获取到了具体的小数位数。

c# folat, double, decimal分析

 

 

所以假如在有效位数里面,decimal是不会有精度损失的,财务计算以及位数比较多的科学计算推荐decimal

相互转换

这边只考虑精度损失的问题,精度比较大的转换成小的有可能会造成精度损失。

float -> double, decimal不会造成精度损失

double-> float可能造成精度损失,double->decimal不会造成精度损失

decimal->float, double可能会造成精度损失

decimal和浮点型的相互转换或者加减乘除操作,必须加强制转换

 

上一篇:python新知识点


下一篇:小数类型选择float、double正确吗?