Bootstrap

为什么金额不能用浮点数来定义呢?

目录

1.int类型的精度丢失

2.计算机中的IEEE浮点数表示法

3.计算机中浮点数的存储结构

4.单精度浮点的数值范围是怎么计算出来的

5.为什么金额不能用浮点数来定义?


1.int类型的精度丢失

       可以发现同样是计算50000*50000的结果,如果用int类型来计算就会得到错误的结果,这是为什么呢?这个错误的结果是怎么来的呢?我们把50000*50000的结果转换为二进制:

0 1001 0101 0000 0010 1111 1001 0000 0000    结果为33位,因int类型为4字节,所以发生溢出

   1001 0101 0000 0010 1111 1001 0000 0000    去掉最高位后此数变为负数

     110 1010 1111 1101 0000 0110 1111 1111      除去最高位,其他位按位取反计算其原码

+                                                                   1
    110 1010 1111 1101 0000 0111 0000 0000

  1110 1010 1111 1101 0000 0111 0000 0000    加上最前面的符号位,结果为:-1794967296,即为上图红框中的错误结果。反观上图结果,我们可以发现把结果定义为同样是四个字节的float类型结果就是正确的,这是为什么呢?聊这个问题之前,我们先要聊一下计算机中的IEEE浮点数表示法。

2.计算机中的IEEE浮点数表示法

       IEEE浮点标准:V=(-1)^s*M*2^E

       s:符号位(sign),0代表此数结果为正数,1代表此数结果为负数

       frac:尾数(significand) ,以原码形式存储,其位数决定小数精度,用M表示

       E:阶码(exponent)值,位数决定数据的范围,规格化的数阶码被解释为以偏置(biased)形式表示的有符号整数,其实就是一个无符号数e,在进行运算时将E=e-Bias(1<=e<=254)

       偏移量:Bias(2^(k-1)-1),k为阶码位的位数

       有个疑问,计算机是怎么知道这个小数点到底是点在哪个位置的呢?有个规定,在该数存入计算机之前,必须对其进行规格化处理:修改阶码同时左右移小数点位置。举个例子:1001.1001=1001.1001*2^0 = 1.0011011*2^3。了解规格化后我们来看下计算机中浮点数的存储结构究竟是什么样的?

3.计算机中浮点数的存储结构

       计算机中浮点数的存储结构如下图所示(图片借鉴自:https://blog.csdn.net/linda_ds/article/details/78136316):

       根据解码的值,被编码的值可以分为四种情况:

       规格化的:表示一般的数,即1<=e<=254时,E=e-Bais,V=(-1)^1.M*2^(e-127)

       非规格化的:表示靠近0的数或0,即e=0,M!=0时,E=1-Bais,V=(-1)^1.M*2^(e-127)

       无穷大:阶码都为1的,当尾数为0时表示无穷大(即溢出)

       不存在:阶码都为1的,当尾数不为0时表示不存在的数,例如-1开根号之类的

4.单精度浮点的数值范围是怎么计算出来的

       根据上面所说,单精度浮点数最大为S=0,e=11111110(254),M=111111111111111111111111(24个1),

       E=e-(2^8-1)=127

       V=(-1)^S*1.M*2^(e-127)=1.8388607*2^12,约等于3.4*10^38。因为3.4*10^38这个数大于int的最大值(2^31-1),所以最上面代码不会出现问题。

5.为什么金额不能用浮点数来定义?

       再看这样一段代码:

       一个双精度浮点数,加了10亿之后,居然没有发生任何变化,这是因为Long的最大值是2^64-1,需要63个二进制位表示,即便是double,52位的尾数也无法完整的表示Long的最大值,不能表示的部分只能被舍去。对于金额来说,舍去不能表示的部分也就产生了精度损失。至于如何避免,用BigDecimal吧。

;