前言 相信不少人(其实我觉得应该是每个人)都遇到过一个问题,那就是当服务端返回的JSON数据中出现了小数时,客户端用CGFloat去解析时总是会出现精度丢失的问题,尤其当遇到敏感
前言相信不少人(其实我觉得应该是每个人)都遇到过一个问题,那就是当服务端返回的JSON数据中出现了小数时,客户端用CGFloat去解析时总是会出现精度丢失的问题,尤其当遇到敏感数据时,这种精度丢失是完全不能被容忍的,本文会从简单的解决方案和原理出发,一起带大家回顾一下这个其实大家以前都学过但是都忘的差不多了的小问题。 如何解决浮点型精度问题四舍五入处理
这个时候呢,作为一个咸鱼开发者,可以写下如下代码:
当然了,还有apple专门为精度问题提供的 NSDecimalNumber类型也可以解决这个问题。NSDecimalNumber的用法非常简单。(至于怎么个简单,请自行百度,别问我,我不百度也写不出来) 更优的解决方案
好的,重头戏来了,接下来,我们上代码!
是的,我会选择不解决这个问题,把皮球踢出去,这才是一个合格的开发者该做的事情嘛! 精度丢失的原因解决浮点精度问题是一个方面,但是如此简单的内容不足以我水完一整篇文章,那么接下来,我们来讲讲为什么好好的数字解析出来,他咋就不准确了呢? 可能很多人都能大概讲出来精度丢失是因为浮点数存储方式的问题,毕竟这玩意儿其实专业对口的筒子们在校的时候都学过,但是大家摸摸自己的小脑袋,嘿,是不是和我一样?全忘光了? 而且鉴于总有些基础很牛逼的面试官和刚好复习过这部分内容的装逼面试官就是喜欢挑这些小问题来刁难咱们这些老年摸鱼程序员,下面我们就来复习一下这部分的知识吧。 浮点类型的存储方式浮点类型在计算机中的存储方式是以科学计数法的方式来存储的:
我们以8.3为例子,要存储8.3 (8.3 x 10^0), 首先肯定要将8.3转化为2进制,8转为二进制时1000,那么0.3呢?
经过上面一番复习我们知道8.3的二进制可表示为 1000.0 1001 1001 1001... (1001无限循环) 用科学计数法表示则为, 1.000 0 1001 1001 1001 x 2^3
而我们知道float是4(32位)个字节,在存储时他的每个bit是如下分配的
有效位数可以看到尾数部分有23位,但是由于是科学技术法之后任何一个数字都可以用 1.aaa x 2^b 这种形式来表示,所以1可以省略,(这个时候就有人要抬杠了,为啥,那遇到0.aaa x 2^b这种咋办呀! 答曰:b可以是负数啊),那么总共23位的数据实际可以表达的位数其实就是24位。
虽然大于16777215的数字也有部分可以精确表达,但是我们谈精度的话肯定就要精确了,那么能精确表达的数字就只有0-16777215之间的了,16777215之间我们数一下,一共是8位,但是由于最高位1开头并不能全部包含,所以说精度应该是7位有效数子。 另外提一下浮点数的表达范围,这个范围肯定是由指数来确定了,具体是多少我就不算了,大家有兴趣的可以去算一算。 指数的存储方式:移位存储可以看到指数部分一共是给了8个bit位,由于指数有正负,那么假设我们第一位表示符号位,那么我们可以表示的数字范围为 -127 ~ +127 那么能表示的范围被分为两部分:
很明显,如果这样会出现一个 -0 和一个 +0,为了避免这个问题所以出现了移位存储:即如果最高位不用来表示符号位,8个bit 可以存储的范围是 0-255,我们重新来规划下两个区间:
这样一来规避了+-0的问题, 上文中所说的8.3 转化为小数后的指数位是3,那么3实际存储的时130,也就是 10000010,我们更新一下实际存储的表格
double类型double类型是8字节64位,其表示的位数和float所不同只有位数差别,其他都一样,双精度表示的位数如下:
总结:输出结果丢失精度原因回到最初的1.9这个数字打印为1.8999999999999999,现在我们应该知道为什么了,因为1.9转2进制时是个无限循环,由于存储的原因后面的循环部分被只被截取了23位,还原回来的十进制数字和原先肯定就不一样了。 同理,如果小数点后面是.5这种5的倍数的小数,打印出来的小数一般都会是正常的,因为.5转2进制时并不是无限循环的。 |
2019-12-11
2021-06-07
2021-10-11
2021-10-10
2021-10-11