iOS恶心的Double精度

作者: foolishBoy | 来源:发表于2018-06-04 21:29 被阅读171次

    最近发现在iOS中将String类型转化为Double类型的时候会有莫名奇妙的精度丢失问题,甚至在Double与Double之间的乘除运算结果也会出现很奇葩的精度问题。试过Objective-C和Swift,都有同样的问题,看来是iOS系统底层对这些情况有特殊处理吧。

    下面以一个很常见的场景来说明问题:

    假如在你的项目中需要提供给用户“提现”的功能,并收取一定比例的手续费(相信玩过各大比特币交易所的同学深有感触)。要求金额保留两位小数,并以手续费四舍五入为准,计算实际提取金额。

    创建很简单的视图:

    提现

    我们输入提现金额,并实时计算手续费,那么我们可能会这样写:

    屏幕快照 2018-06-04 下午8.32.54.png

    得到的结果是:

    结果1

    发现问题了吗?

    按道理,我们输入1234.1的时候,手续费四舍五入保留两位小数应该是61.71,但这里结果是61.70,而且实际提取的金额和手续费的金额加起来也不等于1234.1。

    看图中的断点,是不是很惊讶!为什么inputValue的值是1234.0999999?这就是题目中说的恶心的精度。48行直接将String类型转Double类型就会出现这样奇怪的问题,然后计算的feeValue就相应的变小了一点点,再四舍五入就少了0.01了,同样地,第55行的结果是1172.39499,四舍五入之后变成了1172.39。所以,总的来说,手续费不对,实际提取的也不对了。

    怎么办呢? 有两种方法: 一是手动修正手续费的值;二是使用NSDecimalNumber,也是最推荐的方式。

    首先第一种方法,既然我们知道系统会丢失一点精度,那我们在转化之后手动修复一点点,比如给inputValue加上一个很小的小数,然后再对feeValue手动四舍五入,这样就保证了feeValue的值是正确的,也能保证realValue四舍五入的结果是对的:

    手动修正精度

    第二种方法是推荐使用的,在处理金钱相关的精度问题首先应该考虑使用它

    使用NSDecimalNumber

    其中我们所有的操作都是基于String和NSDecimalNumber的,中间不涉及Double的转换。其中NSDecimalNumberHandler 是可以定义一些行为参数的:

    scale : 需要保留的精度。
    raiseOnExactness : 为YES时在处理精确时如果有错误,就会抛出异常。
    raiseOnOverflow  : YES时在计算精度向上溢出时会抛出异常,否则返回。
    raiseOnUnderflow : YES时在计算精度向下溢出时会抛出异常,否则返回.
    raiseOnDivideByZero : YES时。当除以0时会抛出异常,否则返回。
    

    这样得到的结果是精确的NSDecimalNumber类型的,我么可以像第56行那样取出他的string格式结果。

    使用这两种方式的结果如下:

    正确结果

    相关文章

      网友评论

      • Lol刀妹:我也遇到过,遇到double需保持警惕
      • 玉思盈蝶:我上次也遇到这问题了,不过我们直接取得保留两位了
        foolishBoy:@玉思盈蝶 你可以像我写的那样测试。 你怎么只保留两位呢?除非你用String表示。要不然最后应该都会成为一个很长的Double吧
        玉思盈蝶:@foolishBoy 两位会自己四舍五入吧。。。好像没啥问题,如果有问题,我就用你这个试试,哈哈
        foolishBoy:@玉思盈蝶 不管四舍五入了?

      本文标题:iOS恶心的Double精度

      本文链接:https://www.haomeiwen.com/subject/zgxusftx.html