十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

浮点数精度陷阱:为何3.14不等于3.14?

浮点数精度陷阱:为何3.14不等于3.14? 计算机中的小数表示方式计算机是这样用二进制表示一个小数的float为例S表示一个小数的正负M表示尾数e表示指数但是实际存放e的时候会对其加127也就是E而e的范围是【-126127】所以E的范围就是【1,254】0和255表示特殊的值。其中因为尾数表示的是小数所以尾数的最低位的权重是2^-1次低位权重是2^-2... .... 以此类推。另外我们可以发现如果十进制的整数部分位数越多那么转换成1.M后其在M中占比也就越多那么真正的十进制小数可以占尾数的比重就越少精度也会越低现在有一个小数3.62我们来把它转化成float类型的计算机存储方式5.62 就是整数3小数0.62 3的二进制是101 纯小数的二进制是这样计算的首先纯小数的二进制权重是-1-2,-3这些 而不是0,1,2,3...那么0.62一定等于或者近似等于(2^-23*x12^-22*x2...2^-1*x23) 如果我们对其不断乘2那么最低位次低位...的权重依此都会变成1 最终只剩下x23,x22,x21...x1。所以对于0.62我们进行以下变换 0.62*2 1.24 -----1 0.24*2 0.48 -----0 0.48*2 0.96 -----0 0.96*2 1.92 -----1 0.92*2 1.84 -----1 ... ... 最终我们会发现这是一个无限小数但我们只能记录23这就是计算机不能存储精准浮点 数的原因所在总之最后我们得出0.62的二进制是10011110101110000101001 所以3.62用二进制表示就是101.10011110101110000101001 转换成科学计数法就是1.0110011110101110000101001 * 2^2 对应的0110011110101110000101001是M2是eE就是2127 129s就是0 然后按顺序放在float变量就好了而读取一个数就是相反的过程。另外我们可以谈谈为什么E的范围不包括0和255因为他两表示一种特殊值。想必读者已经发现了表示浮点数的时候一定要把它转换成1.M因此可表示的浮点数的最小值就是1*2^(-126)。如果E为0那么小数的表示就变成了这样浮点数的最小值就可以是0.00...01*2^(-126)所以E为0是用来降低下限降低浮点数可以存储的最小值这叫做非规格化而之前的1.M叫做规格化。E为255表示正无穷也就是最大值计算机中的小数比较从上面的描述中我们可以得知小数的计算机表示不一定是精确的而是可能会有偏差因此两个浮点数比较不能直接用‘’当然如果你直接比较类型相同的浮点数float f 3.14; if(f 3.14f)这样是可以的因为f和3.14f不论是否有偏差他们的存储方式一定是相同的但是你如果这样float f 3.14 if(f 3.14)这是不可以的3.14默认是double类型float和double两种类型的存储精度不一样那么就可能会不相同除此之外这样也是不对的float f 3.14f2.1f if(f 5.34f)在计算过程中f的存储方式可能和5.34f不一样就会导致原本应该相等最后却不相等的结果。所以浮点数的比较应该是带着我们所能容忍的误差的float f 3.14; //判断f是不是等于3.14 if(abs(f-3.14f)1e-6) //1e-6是我们能容忍的最大误差当然上面的叫做绝对误差而如果比较的数太小比如比较0.000001是不是等于0.000002虽然他们的差等于1e-6但是这两个数相差了2倍不应该相等。相对误差就没有这个问题float f 3.14 float g 3.1f0.04f //表示如果两个数的比值之差低于百万分之一就算做相等 if(abs(f-g)/max(abs(f),abs(g)) 1e-6)但是这种方法有一个弊端分母一但为0任何误差都会被放大无数倍直接被判不等另外当分母接近0他们的差虽然非常非常小但由于两个值比较接近所以比值较大导致被判不等。我们的最终解决方案是混合误差float f 3.14 float g 3.1f0.04f if(fabs(f-g) max(fabs(f),fabs(g))*1e-6 || fabs(f-g)1e-30)相对误差用来判断大多数情况绝对误差用来兜底并且用乘法代替除法防止除0异常
返回列表