Swift 中如何避免精度丢失

【iOS专题】Swift 中如何避免精度丢失

        如果你开发过涉及金额计算的 iOS app, 那么你很有可能经历过在使用浮点型数字时精度丢失的问题。实际上,在所有的计算机高级语言中,都存在这样的问题,这是由于计算机使用二进制运算引起的,具体的原因在这里就不探讨了,感兴趣的同学可以参考文末链接或者自行查找资料探索~

先上结论,看图👇🏻👇🏻👇🏻 (不同iOS系统版本下, 浮点数表现可能稍有差异)

测试环境: Xcode 13.2.1 (13C100)


Xcode Playground Test Demo

        可以看到,使用常规的Float和Double数据类型,在处理小数时都可能会出现精度丢失的问题,最常见的后果就是App里的金额显示出现奇怪的小数位,比如demo里的“0.30000000000000004”,这样的数据显示在页面上,可能会造成UI布局上的错乱;数据传给后台校验时,大概率也是无法比对通过的。这时候测试妹子就会赏你一个无情的bug[旺柴]

如何避免精度丢失

1、计算过程中全程使用 Double, 最后转为字符串

        由于 Swift 在精度丢失时会在保留很多位小数 (比如 0.3 存储为 0.30000000000000004), 这些小数与真实值的差距非常之小, 因此我们完全可以在过程中不对其进行任何操作, 仍然让其保持 Double 类型, 在最后时刻要发往服务器或者显示的时候我们将其四舍五入转换为字符串, 这样的结果基本不会出错.

        但是切记一定不要在计算过程中进行四舍五入, 否则极有可能会造成误差的累计, 从而导致误差变大不可接受.

上面的方式简单, 只需要注意在最后时刻进行一次字符串转换即可, 但是有缺陷: 必须让服务器将原本的数字类型转为以字符串类型来接收, 这并不是一种友好的方式. 那么我们到底有没有办法让 app 向服务器发送一个带有精度不丢失的浮点数字的 json 数据包呢? 比如 {"amount": 0.3}, 而不是 {"amount": 0.30000000000000004}

        答案是可以。

2、以 Decimal 格式进行接收并计算

        Swift 为我们提供了用于十进制计算的一个类型: Decimal, 这个类型也带有 +, -, *, / 运算符, 并且支持 Codable 协议, 我们完全可以定义此类型接受服务器的参数值, 然后以此类型进行运算然后使用, 最后, 因为其支持 Codable 协议, 我们可以将其值直接放入 json 包中. 没有特殊情况的话我们就完全避开了二进制浮点型数字了, 这样是不会有任何的误差的

正确使用 Decimal 的初始化方式

        Decimal 有多种初始化方式, 我们可以传入整型值, 传入浮点型, 传入字符串方式进行初始化, 我认为正确的初始化方式应该是使用字符串.

Decimal的初始化

        上面这张图应该很简单明了的说明了原因. 因为我们传入 Double 时, Swift 对其进行了一次承载, 这一次承载就对其造成了精度丢失, 根据已经丢失精度的 Double 初始化出 Decimal, 这个 Decimal 是精度丢失的也就不难理解了


参考:

1. 浮点数精度问题原因以及各种现象的解释

2.计算机组成原理:浮点精度运算不精确的原因

3.计算机中的浮点数运算“陷阱”

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。